backend/datasources: move datasources models into the datasources service package (#51267)
* backend/datasources: move datasources models into the datasources service pkg
This commit is contained in:
+2
-1
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
@@ -151,7 +152,7 @@ func (hs *HTTPServer) AlertTest(c *models.ReqContext) response.Response {
|
||||
if errors.As(err, &validationErr) {
|
||||
return response.Error(422, validationErr.Error(), nil)
|
||||
}
|
||||
if errors.Is(err, models.ErrDataSourceAccessDenied) {
|
||||
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
|
||||
return response.Error(403, "Access denied to datasource", err)
|
||||
}
|
||||
return response.Error(500, "Failed to test rule", err)
|
||||
|
||||
@@ -11,25 +11,25 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/service"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/query"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
|
||||
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||
)
|
||||
|
||||
func TestAPIGetPublicDashboard(t *testing.T) {
|
||||
@@ -259,7 +259,7 @@ func TestAPIQueryPublicDashboard(t *testing.T) {
|
||||
qds := query.ProvideService(
|
||||
nil,
|
||||
&fakeDatasources.FakeCacheService{
|
||||
DataSources: []*models.DataSource{
|
||||
DataSources: []*datasources.DataSource{
|
||||
{Uid: "mysqlds"},
|
||||
{Uid: "promds"},
|
||||
{Uid: "promds2"},
|
||||
@@ -547,12 +547,12 @@ func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T)
|
||||
)
|
||||
scenario.hs.queryDataService = qds
|
||||
|
||||
_ = db.AddDataSource(context.Background(), &models.AddDataSourceCommand{
|
||||
_ = db.AddDataSource(context.Background(), &datasources.AddDataSourceCommand{
|
||||
Uid: "ds1",
|
||||
OrgId: 1,
|
||||
Name: "laban",
|
||||
Type: models.DS_MYSQL,
|
||||
Access: models.DS_ACCESS_DIRECT,
|
||||
Type: datasources.DS_MYSQL,
|
||||
Access: datasources.DS_ACCESS_DIRECT,
|
||||
Url: "http://test",
|
||||
Database: "site",
|
||||
ReadOnly: true,
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/tsdb/mssql"
|
||||
)
|
||||
|
||||
@@ -16,20 +16,20 @@ var logger = log.New("datasource")
|
||||
|
||||
// requiredURL contains the set of data sources that require a URL.
|
||||
var requiredURL = map[string]bool{
|
||||
models.DS_GRAPHITE: true,
|
||||
models.DS_INFLUXDB: true,
|
||||
models.DS_INFLUXDB_08: true,
|
||||
models.DS_ES: true,
|
||||
models.DS_PROMETHEUS: true,
|
||||
models.DS_ALERTMANAGER: true,
|
||||
models.DS_JAEGER: true,
|
||||
models.DS_LOKI: true,
|
||||
models.DS_OPENTSDB: true,
|
||||
models.DS_TEMPO: true,
|
||||
models.DS_ZIPKIN: true,
|
||||
models.DS_MYSQL: true,
|
||||
models.DS_POSTGRES: true,
|
||||
models.DS_MSSQL: true,
|
||||
datasources.DS_GRAPHITE: true,
|
||||
datasources.DS_INFLUXDB: true,
|
||||
datasources.DS_INFLUXDB_08: true,
|
||||
datasources.DS_ES: true,
|
||||
datasources.DS_PROMETHEUS: true,
|
||||
datasources.DS_ALERTMANAGER: true,
|
||||
datasources.DS_JAEGER: true,
|
||||
datasources.DS_LOKI: true,
|
||||
datasources.DS_OPENTSDB: true,
|
||||
datasources.DS_TEMPO: true,
|
||||
datasources.DS_ZIPKIN: true,
|
||||
datasources.DS_MYSQL: true,
|
||||
datasources.DS_POSTGRES: true,
|
||||
datasources.DS_MSSQL: true,
|
||||
}
|
||||
|
||||
// URLValidationError represents an error from validating a data source URL.
|
||||
|
||||
+45
-44
@@ -10,6 +10,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/datasource"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
@@ -24,10 +25,10 @@ import (
|
||||
)
|
||||
|
||||
var datasourcesLogger = log.New("datasources")
|
||||
var secretsPluginError models.ErrDatasourceSecretsPluginUserFriendly
|
||||
var secretsPluginError datasources.ErrDatasourceSecretsPluginUserFriendly
|
||||
|
||||
func (hs *HTTPServer) GetDataSources(c *models.ReqContext) response.Response {
|
||||
query := models.GetDataSourcesQuery{OrgId: c.OrgId, DataSourceLimit: hs.Cfg.DataSourceLimit}
|
||||
query := datasources.GetDataSourcesQuery{OrgId: c.OrgId, DataSourceLimit: hs.Cfg.DataSourceLimit}
|
||||
|
||||
if err := hs.DataSourcesService.GetDataSources(c.Req.Context(), &query); err != nil {
|
||||
return response.Error(500, "Failed to query datasources", err)
|
||||
@@ -78,16 +79,16 @@ func (hs *HTTPServer) GetDataSourceById(c *models.ReqContext) response.Response
|
||||
if err != nil {
|
||||
return response.Error(http.StatusBadRequest, "id is invalid", err)
|
||||
}
|
||||
query := models.GetDataSourceQuery{
|
||||
query := datasources.GetDataSourceQuery{
|
||||
Id: id,
|
||||
OrgId: c.OrgId,
|
||||
}
|
||||
|
||||
if err := hs.DataSourcesService.GetDataSource(c.Req.Context(), &query); err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(404, "Data source not found", nil)
|
||||
}
|
||||
if errors.Is(err, models.ErrDataSourceIdentifierNotSet) {
|
||||
if errors.Is(err, datasources.ErrDataSourceIdentifierNotSet) {
|
||||
return response.Error(400, "Datasource id is missing", nil)
|
||||
}
|
||||
return response.Error(500, "Failed to query datasources", err)
|
||||
@@ -114,7 +115,7 @@ func (hs *HTTPServer) DeleteDataSourceById(c *models.ReqContext) response.Respon
|
||||
|
||||
ds, err := hs.getRawDataSourceById(c.Req.Context(), id, c.OrgId)
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(404, "Data source not found", nil)
|
||||
}
|
||||
return response.Error(400, "Failed to delete datasource", nil)
|
||||
@@ -124,7 +125,7 @@ func (hs *HTTPServer) DeleteDataSourceById(c *models.ReqContext) response.Respon
|
||||
return response.Error(403, "Cannot delete read-only data source", nil)
|
||||
}
|
||||
|
||||
cmd := &models.DeleteDataSourceCommand{ID: id, OrgID: c.OrgId, Name: ds.Name}
|
||||
cmd := &datasources.DeleteDataSourceCommand{ID: id, OrgID: c.OrgId, Name: ds.Name}
|
||||
|
||||
err = hs.DataSourcesService.DeleteDataSource(c.Req.Context(), cmd)
|
||||
if err != nil {
|
||||
@@ -144,7 +145,7 @@ func (hs *HTTPServer) GetDataSourceByUID(c *models.ReqContext) response.Response
|
||||
ds, err := hs.getRawDataSourceByUID(c.Req.Context(), web.Params(c.Req)[":uid"], c.OrgId)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(http.StatusNotFound, "Data source not found", nil)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Failed to query datasource", err)
|
||||
@@ -168,7 +169,7 @@ func (hs *HTTPServer) DeleteDataSourceByUID(c *models.ReqContext) response.Respo
|
||||
|
||||
ds, err := hs.getRawDataSourceByUID(c.Req.Context(), uid, c.OrgId)
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(404, "Data source not found", nil)
|
||||
}
|
||||
return response.Error(400, "Failed to delete datasource", nil)
|
||||
@@ -178,7 +179,7 @@ func (hs *HTTPServer) DeleteDataSourceByUID(c *models.ReqContext) response.Respo
|
||||
return response.Error(403, "Cannot delete read-only data source", nil)
|
||||
}
|
||||
|
||||
cmd := &models.DeleteDataSourceCommand{UID: uid, OrgID: c.OrgId, Name: ds.Name}
|
||||
cmd := &datasources.DeleteDataSourceCommand{UID: uid, OrgID: c.OrgId, Name: ds.Name}
|
||||
|
||||
err = hs.DataSourcesService.DeleteDataSource(c.Req.Context(), cmd)
|
||||
if err != nil {
|
||||
@@ -204,9 +205,9 @@ func (hs *HTTPServer) DeleteDataSourceByName(c *models.ReqContext) response.Resp
|
||||
return response.Error(400, "Missing valid datasource name", nil)
|
||||
}
|
||||
|
||||
getCmd := &models.GetDataSourceQuery{Name: name, OrgId: c.OrgId}
|
||||
getCmd := &datasources.GetDataSourceQuery{Name: name, OrgId: c.OrgId}
|
||||
if err := hs.DataSourcesService.GetDataSource(c.Req.Context(), getCmd); err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(404, "Data source not found", nil)
|
||||
}
|
||||
return response.Error(500, "Failed to delete datasource", err)
|
||||
@@ -216,7 +217,7 @@ func (hs *HTTPServer) DeleteDataSourceByName(c *models.ReqContext) response.Resp
|
||||
return response.Error(403, "Cannot delete read-only data source", nil)
|
||||
}
|
||||
|
||||
cmd := &models.DeleteDataSourceCommand{Name: name, OrgID: c.OrgId}
|
||||
cmd := &datasources.DeleteDataSourceCommand{Name: name, OrgID: c.OrgId}
|
||||
err := hs.DataSourcesService.DeleteDataSource(c.Req.Context(), cmd)
|
||||
if err != nil {
|
||||
if errors.As(err, &secretsPluginError) {
|
||||
@@ -243,7 +244,7 @@ func validateURL(cmdType string, url string) response.Response {
|
||||
|
||||
// POST /api/datasources/
|
||||
func (hs *HTTPServer) AddDataSource(c *models.ReqContext) response.Response {
|
||||
cmd := models.AddDataSourceCommand{}
|
||||
cmd := datasources.AddDataSourceCommand{}
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
@@ -258,7 +259,7 @@ func (hs *HTTPServer) AddDataSource(c *models.ReqContext) response.Response {
|
||||
}
|
||||
|
||||
if err := hs.DataSourcesService.AddDataSource(c.Req.Context(), &cmd); err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNameExists) || errors.Is(err, models.ErrDataSourceUidExists) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNameExists) || errors.Is(err, datasources.ErrDataSourceUidExists) {
|
||||
return response.Error(409, err.Error(), err)
|
||||
}
|
||||
|
||||
@@ -280,7 +281,7 @@ func (hs *HTTPServer) AddDataSource(c *models.ReqContext) response.Response {
|
||||
|
||||
// PUT /api/datasources/:id
|
||||
func (hs *HTTPServer) UpdateDataSourceByID(c *models.ReqContext) response.Response {
|
||||
cmd := models.UpdateDataSourceCommand{}
|
||||
cmd := datasources.UpdateDataSourceCommand{}
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
@@ -296,7 +297,7 @@ func (hs *HTTPServer) UpdateDataSourceByID(c *models.ReqContext) response.Respon
|
||||
|
||||
ds, err := hs.getRawDataSourceById(c.Req.Context(), cmd.Id, cmd.OrgId)
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(404, "Data source not found", nil)
|
||||
}
|
||||
return response.Error(500, "Failed to update datasource", err)
|
||||
@@ -306,7 +307,7 @@ func (hs *HTTPServer) UpdateDataSourceByID(c *models.ReqContext) response.Respon
|
||||
|
||||
// PUT /api/datasources/:uid
|
||||
func (hs *HTTPServer) UpdateDataSourceByUID(c *models.ReqContext) response.Response {
|
||||
cmd := models.UpdateDataSourceCommand{}
|
||||
cmd := datasources.UpdateDataSourceCommand{}
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
@@ -318,7 +319,7 @@ func (hs *HTTPServer) UpdateDataSourceByUID(c *models.ReqContext) response.Respo
|
||||
|
||||
ds, err := hs.getRawDataSourceByUID(c.Req.Context(), web.Params(c.Req)[":uid"], c.OrgId)
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(http.StatusNotFound, "Data source not found", nil)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Failed to update datasource", err)
|
||||
@@ -327,14 +328,14 @@ func (hs *HTTPServer) UpdateDataSourceByUID(c *models.ReqContext) response.Respo
|
||||
return hs.updateDataSourceByID(c, ds, cmd)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) updateDataSourceByID(c *models.ReqContext, ds *models.DataSource, cmd models.UpdateDataSourceCommand) response.Response {
|
||||
func (hs *HTTPServer) updateDataSourceByID(c *models.ReqContext, ds *datasources.DataSource, cmd datasources.UpdateDataSourceCommand) response.Response {
|
||||
if ds.ReadOnly {
|
||||
return response.Error(403, "Cannot update read-only data source", nil)
|
||||
}
|
||||
|
||||
err := hs.DataSourcesService.UpdateDataSource(c.Req.Context(), &cmd)
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceUpdatingOldVersion) {
|
||||
if errors.Is(err, datasources.ErrDataSourceUpdatingOldVersion) {
|
||||
return response.Error(409, "Datasource has already been updated by someone else. Please reload and try again", err)
|
||||
}
|
||||
|
||||
@@ -344,13 +345,13 @@ func (hs *HTTPServer) updateDataSourceByID(c *models.ReqContext, ds *models.Data
|
||||
return response.Error(500, "Failed to update datasource", err)
|
||||
}
|
||||
|
||||
query := models.GetDataSourceQuery{
|
||||
query := datasources.GetDataSourceQuery{
|
||||
Id: cmd.Id,
|
||||
OrgId: c.OrgId,
|
||||
}
|
||||
|
||||
if err := hs.DataSourcesService.GetDataSource(c.Req.Context(), &query); err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(404, "Data source not found", nil)
|
||||
}
|
||||
return response.Error(500, "Failed to query datasource", err)
|
||||
@@ -368,8 +369,8 @@ func (hs *HTTPServer) updateDataSourceByID(c *models.ReqContext, ds *models.Data
|
||||
})
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) getRawDataSourceById(ctx context.Context, id int64, orgID int64) (*models.DataSource, error) {
|
||||
query := models.GetDataSourceQuery{
|
||||
func (hs *HTTPServer) getRawDataSourceById(ctx context.Context, id int64, orgID int64) (*datasources.DataSource, error) {
|
||||
query := datasources.GetDataSourceQuery{
|
||||
Id: id,
|
||||
OrgId: orgID,
|
||||
}
|
||||
@@ -381,8 +382,8 @@ func (hs *HTTPServer) getRawDataSourceById(ctx context.Context, id int64, orgID
|
||||
return query.Result, nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) getRawDataSourceByUID(ctx context.Context, uid string, orgID int64) (*models.DataSource, error) {
|
||||
query := models.GetDataSourceQuery{
|
||||
func (hs *HTTPServer) getRawDataSourceByUID(ctx context.Context, uid string, orgID int64) (*datasources.DataSource, error) {
|
||||
query := datasources.GetDataSourceQuery{
|
||||
Uid: uid,
|
||||
OrgId: orgID,
|
||||
}
|
||||
@@ -396,10 +397,10 @@ func (hs *HTTPServer) getRawDataSourceByUID(ctx context.Context, uid string, org
|
||||
|
||||
// Get /api/datasources/name/:name
|
||||
func (hs *HTTPServer) GetDataSourceByName(c *models.ReqContext) response.Response {
|
||||
query := models.GetDataSourceQuery{Name: web.Params(c.Req)[":name"], OrgId: c.OrgId}
|
||||
query := datasources.GetDataSourceQuery{Name: web.Params(c.Req)[":name"], OrgId: c.OrgId}
|
||||
|
||||
if err := hs.DataSourcesService.GetDataSource(c.Req.Context(), &query); err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(404, "Data source not found", nil)
|
||||
}
|
||||
return response.Error(500, "Failed to query datasources", err)
|
||||
@@ -411,10 +412,10 @@ func (hs *HTTPServer) GetDataSourceByName(c *models.ReqContext) response.Respons
|
||||
|
||||
// Get /api/datasources/id/:name
|
||||
func (hs *HTTPServer) GetDataSourceIdByName(c *models.ReqContext) response.Response {
|
||||
query := models.GetDataSourceQuery{Name: web.Params(c.Req)[":name"], OrgId: c.OrgId}
|
||||
query := datasources.GetDataSourceQuery{Name: web.Params(c.Req)[":name"], OrgId: c.OrgId}
|
||||
|
||||
if err := hs.DataSourcesService.GetDataSource(c.Req.Context(), &query); err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(404, "Data source not found", nil)
|
||||
}
|
||||
return response.Error(500, "Failed to query datasources", err)
|
||||
@@ -437,7 +438,7 @@ func (hs *HTTPServer) CallDatasourceResource(c *models.ReqContext) {
|
||||
}
|
||||
ds, err := hs.DataSourceCache.GetDatasource(c.Req.Context(), datasourceID, c.SignedInUser, c.SkipCache)
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceAccessDenied) {
|
||||
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
|
||||
c.JsonApiErr(403, "Access denied to datasource", err)
|
||||
return
|
||||
}
|
||||
@@ -464,7 +465,7 @@ func (hs *HTTPServer) CallDatasourceResourceWithUID(c *models.ReqContext) {
|
||||
|
||||
ds, err := hs.DataSourceCache.GetDatasourceByUID(c.Req.Context(), dsUID, c.SignedInUser, c.SkipCache)
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceAccessDenied) {
|
||||
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
|
||||
c.JsonApiErr(http.StatusForbidden, "Access denied to datasource", err)
|
||||
return
|
||||
}
|
||||
@@ -481,7 +482,7 @@ func (hs *HTTPServer) CallDatasourceResourceWithUID(c *models.ReqContext) {
|
||||
hs.callPluginResourceWithDataSource(c, plugin.ID, ds)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) convertModelToDtos(ctx context.Context, ds *models.DataSource) dtos.DataSource {
|
||||
func (hs *HTTPServer) convertModelToDtos(ctx context.Context, ds *datasources.DataSource) dtos.DataSource {
|
||||
dto := dtos.DataSource{
|
||||
Id: ds.Id,
|
||||
UID: ds.Uid,
|
||||
@@ -526,7 +527,7 @@ func (hs *HTTPServer) CheckDatasourceHealthWithUID(c *models.ReqContext) respons
|
||||
|
||||
ds, err := hs.DataSourceCache.GetDatasourceByUID(c.Req.Context(), dsUID, c.SignedInUser, c.SkipCache)
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceAccessDenied) {
|
||||
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
|
||||
return response.Error(http.StatusForbidden, "Access denied to datasource", err)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Unable to load datasource metadata", err)
|
||||
@@ -544,7 +545,7 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) response.Respo
|
||||
|
||||
ds, err := hs.DataSourceCache.GetDatasource(c.Req.Context(), datasourceID, c.SignedInUser, c.SkipCache)
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrDataSourceAccessDenied) {
|
||||
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
|
||||
return response.Error(http.StatusForbidden, "Access denied to datasource", err)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Unable to load datasource metadata", err)
|
||||
@@ -552,7 +553,7 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) response.Respo
|
||||
return hs.checkDatasourceHealth(c, ds)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) checkDatasourceHealth(c *models.ReqContext, ds *models.DataSource) response.Response {
|
||||
func (hs *HTTPServer) checkDatasourceHealth(c *models.ReqContext, ds *datasources.DataSource) response.Response {
|
||||
plugin, exists := hs.pluginStore.Plugin(c.Req.Context(), ds.Type)
|
||||
if !exists {
|
||||
return response.Error(http.StatusInternalServerError, "Unable to find datasource plugin", nil)
|
||||
@@ -625,8 +626,8 @@ func (hs *HTTPServer) checkDatasourceHealth(c *models.ReqContext, ds *models.Dat
|
||||
return response.JSON(http.StatusOK, payload)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) decryptSecureJsonDataFn(ctx context.Context) func(ds *models.DataSource) map[string]string {
|
||||
return func(ds *models.DataSource) map[string]string {
|
||||
func (hs *HTTPServer) decryptSecureJsonDataFn(ctx context.Context) func(ds *datasources.DataSource) map[string]string {
|
||||
return func(ds *datasources.DataSource) map[string]string {
|
||||
decryptedJsonData, err := hs.DataSourcesService.DecryptedValues(ctx, ds)
|
||||
if err != nil {
|
||||
hs.log.Error("Failed to decrypt secure json data", "error", err)
|
||||
@@ -635,18 +636,18 @@ func (hs *HTTPServer) decryptSecureJsonDataFn(ctx context.Context) func(ds *mode
|
||||
}
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) filterDatasourcesByQueryPermission(ctx context.Context, user *models.SignedInUser, datasources []*models.DataSource) ([]*models.DataSource, error) {
|
||||
query := models.DatasourcesPermissionFilterQuery{
|
||||
func (hs *HTTPServer) filterDatasourcesByQueryPermission(ctx context.Context, user *models.SignedInUser, ds []*datasources.DataSource) ([]*datasources.DataSource, error) {
|
||||
query := datasources.DatasourcesPermissionFilterQuery{
|
||||
User: user,
|
||||
Datasources: datasources,
|
||||
Datasources: ds,
|
||||
}
|
||||
query.Result = datasources
|
||||
query.Result = ds
|
||||
|
||||
if err := hs.DatasourcePermissionsService.FilterDatasourcesBasedOnQueryPermissions(ctx, &query); err != nil {
|
||||
if !errors.Is(err, permissions.ErrNotImplemented) {
|
||||
return nil, err
|
||||
}
|
||||
return datasources, nil
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
return query.Result, nil
|
||||
|
||||
+25
-25
@@ -34,7 +34,7 @@ func TestDataSourcesProxy_userLoggedIn(t *testing.T) {
|
||||
mockDatasourcePermissionService := permissions.NewMockDatasourcePermissionService()
|
||||
loggedInUserScenario(t, "When calling GET on", "/api/datasources/", "/api/datasources/", func(sc *scenarioContext) {
|
||||
// Stubs the database query
|
||||
ds := []*models.DataSource{
|
||||
ds := []*datasources.DataSource{
|
||||
{Name: "mmm"},
|
||||
{Name: "ZZZ"},
|
||||
{Name: "BBB"},
|
||||
@@ -85,7 +85,7 @@ func TestAddDataSource_InvalidURL(t *testing.T) {
|
||||
}
|
||||
|
||||
sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||
c.Req.Body = mockRequestBody(models.AddDataSourceCommand{
|
||||
c.Req.Body = mockRequestBody(datasources.AddDataSourceCommand{
|
||||
Name: "Test",
|
||||
Url: "invalid:url",
|
||||
Access: "direct",
|
||||
@@ -106,14 +106,14 @@ func TestAddDataSource_URLWithoutProtocol(t *testing.T) {
|
||||
|
||||
hs := &HTTPServer{
|
||||
DataSourcesService: &dataSourcesServiceMock{
|
||||
expectedDatasource: &models.DataSource{},
|
||||
expectedDatasource: &datasources.DataSource{},
|
||||
},
|
||||
}
|
||||
|
||||
sc := setupScenarioContext(t, "/api/datasources")
|
||||
|
||||
sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||
c.Req.Body = mockRequestBody(models.AddDataSourceCommand{
|
||||
c.Req.Body = mockRequestBody(datasources.AddDataSourceCommand{
|
||||
Name: name,
|
||||
Url: url,
|
||||
Access: "direct",
|
||||
@@ -135,7 +135,7 @@ func TestUpdateDataSource_InvalidURL(t *testing.T) {
|
||||
sc := setupScenarioContext(t, "/api/datasources/1234")
|
||||
|
||||
sc.m.Put(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||
c.Req.Body = mockRequestBody(models.AddDataSourceCommand{
|
||||
c.Req.Body = mockRequestBody(datasources.AddDataSourceCommand{
|
||||
Name: "Test",
|
||||
Url: "invalid:url",
|
||||
Access: "direct",
|
||||
@@ -156,14 +156,14 @@ func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) {
|
||||
|
||||
hs := &HTTPServer{
|
||||
DataSourcesService: &dataSourcesServiceMock{
|
||||
expectedDatasource: &models.DataSource{},
|
||||
expectedDatasource: &datasources.DataSource{},
|
||||
},
|
||||
}
|
||||
|
||||
sc := setupScenarioContext(t, "/api/datasources/1234")
|
||||
|
||||
sc.m.Put(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||
c.Req.Body = mockRequestBody(models.AddDataSourceCommand{
|
||||
c.Req.Body = mockRequestBody(datasources.AddDataSourceCommand{
|
||||
Name: name,
|
||||
Url: url,
|
||||
Access: "direct",
|
||||
@@ -178,7 +178,7 @@ func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
testDatasource := models.DataSource{
|
||||
testDatasource := datasources.DataSource{
|
||||
Id: 3,
|
||||
Uid: "testUID",
|
||||
OrgId: testOrgID,
|
||||
@@ -187,7 +187,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
Type: "postgresql",
|
||||
Access: "Proxy",
|
||||
}
|
||||
testDatasourceReadOnly := models.DataSource{
|
||||
testDatasourceReadOnly := datasources.DataSource{
|
||||
Id: 4,
|
||||
Uid: "testUID",
|
||||
OrgId: testOrgID,
|
||||
@@ -199,7 +199,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
}
|
||||
|
||||
addDatasourceBody := func() io.Reader {
|
||||
s, _ := json.Marshal(models.AddDataSourceCommand{
|
||||
s, _ := json.Marshal(datasources.AddDataSourceCommand{
|
||||
Name: "test",
|
||||
Url: "http://localhost:5432",
|
||||
Type: "postgresql",
|
||||
@@ -212,12 +212,12 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
expectedDatasource: &testDatasource,
|
||||
}
|
||||
dsPermissionService := permissions.NewMockDatasourcePermissionService()
|
||||
dsPermissionService.DsResult = []*models.DataSource{
|
||||
dsPermissionService.DsResult = []*datasources.DataSource{
|
||||
&testDatasource,
|
||||
}
|
||||
|
||||
updateDatasourceBody := func() io.Reader {
|
||||
s, _ := json.Marshal(models.UpdateDataSourceCommand{
|
||||
s, _ := json.Marshal(datasources.UpdateDataSourceCommand{
|
||||
Name: "test",
|
||||
Url: "http://localhost:5432",
|
||||
Type: "postgresql",
|
||||
@@ -228,7 +228,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
type acTestCaseWithHandler struct {
|
||||
body func() io.Reader
|
||||
accessControlTestCase
|
||||
expectedDS *models.DataSource
|
||||
expectedDS *datasources.DataSource
|
||||
expectedSQLError error
|
||||
}
|
||||
tests := []acTestCaseWithHandler{
|
||||
@@ -246,7 +246,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedSQLError: models.ErrDataSourceNotFound,
|
||||
expectedSQLError: datasources.ErrDataSourceNotFound,
|
||||
},
|
||||
{
|
||||
accessControlTestCase: accessControlTestCase{
|
||||
@@ -507,7 +507,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
// mock sqlStore and datasource permission service
|
||||
dsServiceMock.expectedError = test.expectedSQLError
|
||||
dsServiceMock.expectedDatasource = test.expectedDS
|
||||
dsPermissionService.DsResult = []*models.DataSource{test.expectedDS}
|
||||
dsPermissionService.DsResult = []*datasources.DataSource{test.expectedDS}
|
||||
if test.expectedDS == nil {
|
||||
dsPermissionService.DsResult = nil
|
||||
}
|
||||
@@ -547,44 +547,44 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
type dataSourcesServiceMock struct {
|
||||
datasources.DataSourceService
|
||||
|
||||
expectedDatasources []*models.DataSource
|
||||
expectedDatasource *models.DataSource
|
||||
expectedDatasources []*datasources.DataSource
|
||||
expectedDatasource *datasources.DataSource
|
||||
expectedError error
|
||||
}
|
||||
|
||||
func (m *dataSourcesServiceMock) GetDataSource(ctx context.Context, query *models.GetDataSourceQuery) error {
|
||||
func (m *dataSourcesServiceMock) GetDataSource(ctx context.Context, query *datasources.GetDataSourceQuery) error {
|
||||
query.Result = m.expectedDatasource
|
||||
return m.expectedError
|
||||
}
|
||||
|
||||
func (m *dataSourcesServiceMock) GetDataSources(ctx context.Context, query *models.GetDataSourcesQuery) error {
|
||||
func (m *dataSourcesServiceMock) GetDataSources(ctx context.Context, query *datasources.GetDataSourcesQuery) error {
|
||||
query.Result = m.expectedDatasources
|
||||
return m.expectedError
|
||||
}
|
||||
|
||||
func (m *dataSourcesServiceMock) GetDataSourcesByType(ctx context.Context, query *models.GetDataSourcesByTypeQuery) error {
|
||||
func (m *dataSourcesServiceMock) GetDataSourcesByType(ctx context.Context, query *datasources.GetDataSourcesByTypeQuery) error {
|
||||
return m.expectedError
|
||||
}
|
||||
|
||||
func (m *dataSourcesServiceMock) GetDefaultDataSource(ctx context.Context, query *models.GetDefaultDataSourceQuery) error {
|
||||
func (m *dataSourcesServiceMock) GetDefaultDataSource(ctx context.Context, query *datasources.GetDefaultDataSourceQuery) error {
|
||||
return m.expectedError
|
||||
}
|
||||
|
||||
func (m *dataSourcesServiceMock) DeleteDataSource(ctx context.Context, cmd *models.DeleteDataSourceCommand) error {
|
||||
func (m *dataSourcesServiceMock) DeleteDataSource(ctx context.Context, cmd *datasources.DeleteDataSourceCommand) error {
|
||||
return m.expectedError
|
||||
}
|
||||
|
||||
func (m *dataSourcesServiceMock) AddDataSource(ctx context.Context, cmd *models.AddDataSourceCommand) error {
|
||||
func (m *dataSourcesServiceMock) AddDataSource(ctx context.Context, cmd *datasources.AddDataSourceCommand) error {
|
||||
cmd.Result = m.expectedDatasource
|
||||
return m.expectedError
|
||||
}
|
||||
|
||||
func (m *dataSourcesServiceMock) UpdateDataSource(ctx context.Context, cmd *models.UpdateDataSourceCommand) error {
|
||||
func (m *dataSourcesServiceMock) UpdateDataSource(ctx context.Context, cmd *datasources.UpdateDataSourceCommand) error {
|
||||
cmd.Result = m.expectedDatasource
|
||||
return m.expectedError
|
||||
}
|
||||
|
||||
func (m *dataSourcesServiceMock) DecryptedValues(ctx context.Context, ds *models.DataSource) (map[string]string, error) {
|
||||
func (m *dataSourcesServiceMock) DecryptedValues(ctx context.Context, ds *datasources.DataSource) (map[string]string, error) {
|
||||
decryptedValues := make(map[string]string)
|
||||
return decryptedValues, m.expectedError
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package definitions
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
)
|
||||
|
||||
// swagger:route GET /datasources datasources getDatasources
|
||||
@@ -457,14 +457,14 @@ type DatasourceProxyPOSTByUIDcallsParams struct {
|
||||
type AddDatasourceParams struct {
|
||||
// in:body
|
||||
// required:true
|
||||
Body models.AddDataSourceCommand
|
||||
Body datasources.AddDataSourceCommand
|
||||
}
|
||||
|
||||
// swagger:parameters updateDatasourceByID
|
||||
type UpdateDatasourceParams struct {
|
||||
// in:body
|
||||
// required:true
|
||||
Body models.UpdateDataSourceCommand
|
||||
Body datasources.UpdateDataSourceCommand
|
||||
// in:path
|
||||
// required:true
|
||||
DatasourceID string `json:"id"`
|
||||
@@ -474,7 +474,7 @@ type UpdateDatasourceParams struct {
|
||||
type UpdateDatasourceByUIDParams struct {
|
||||
// in:body
|
||||
// required:true
|
||||
Body models.UpdateDataSourceCommand
|
||||
Body datasources.UpdateDataSourceCommand
|
||||
// in:path
|
||||
// required:true
|
||||
DatasourceUID string `json:"uid"`
|
||||
|
||||
+17
-17
@@ -4,8 +4,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
)
|
||||
|
||||
type DataSource struct {
|
||||
@@ -15,7 +15,7 @@ type DataSource struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
TypeLogoUrl string `json:"typeLogoUrl"`
|
||||
Access models.DsAccess `json:"access"`
|
||||
Access datasources.DsAccess `json:"access"`
|
||||
Url string `json:"url"`
|
||||
User string `json:"user"`
|
||||
Database string `json:"database"`
|
||||
@@ -31,21 +31,21 @@ type DataSource struct {
|
||||
}
|
||||
|
||||
type DataSourceListItemDTO struct {
|
||||
Id int64 `json:"id"`
|
||||
UID string `json:"uid"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
TypeName string `json:"typeName"`
|
||||
TypeLogoUrl string `json:"typeLogoUrl"`
|
||||
Access models.DsAccess `json:"access"`
|
||||
Url string `json:"url"`
|
||||
User string `json:"user"`
|
||||
Database string `json:"database"`
|
||||
BasicAuth bool `json:"basicAuth"`
|
||||
IsDefault bool `json:"isDefault"`
|
||||
JsonData *simplejson.Json `json:"jsonData,omitempty"`
|
||||
ReadOnly bool `json:"readOnly"`
|
||||
Id int64 `json:"id"`
|
||||
UID string `json:"uid"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
TypeName string `json:"typeName"`
|
||||
TypeLogoUrl string `json:"typeLogoUrl"`
|
||||
Access datasources.DsAccess `json:"access"`
|
||||
Url string `json:"url"`
|
||||
User string `json:"user"`
|
||||
Database string `json:"database"`
|
||||
BasicAuth bool `json:"basicAuth"`
|
||||
IsDefault bool `json:"isDefault"`
|
||||
JsonData *simplejson.Json `json:"jsonData,omitempty"`
|
||||
ReadOnly bool `json:"readOnly"`
|
||||
}
|
||||
|
||||
type DataSourceList []DataSourceListItemDTO
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -191,10 +192,10 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) getFSDataSources(c *models.ReqContext, enabledPlugins EnabledPlugins) (map[string]plugins.DataSourceDTO, error) {
|
||||
orgDataSources := make([]*models.DataSource, 0)
|
||||
orgDataSources := make([]*datasources.DataSource, 0)
|
||||
|
||||
if c.OrgId != 0 {
|
||||
query := models.GetDataSourcesQuery{OrgId: c.OrgId, DataSourceLimit: hs.Cfg.DataSourceLimit}
|
||||
query := datasources.GetDataSourcesQuery{OrgId: c.OrgId, DataSourceLimit: hs.Cfg.DataSourceLimit}
|
||||
err := hs.SQLStore.GetDataSources(c.Req.Context(), &query)
|
||||
|
||||
if err != nil {
|
||||
@@ -214,7 +215,7 @@ func (hs *HTTPServer) getFSDataSources(c *models.ReqContext, enabledPlugins Enab
|
||||
for _, ds := range orgDataSources {
|
||||
url := ds.Url
|
||||
|
||||
if ds.Access == models.DS_ACCESS_PROXY {
|
||||
if ds.Access == datasources.DS_ACCESS_PROXY {
|
||||
url = "/api/datasources/proxy/" + strconv.FormatInt(ds.Id, 10)
|
||||
}
|
||||
|
||||
@@ -248,7 +249,7 @@ func (hs *HTTPServer) getFSDataSources(c *models.ReqContext, enabledPlugins Enab
|
||||
dsDTO.JSONData = ds.JsonData.MustMap()
|
||||
}
|
||||
|
||||
if ds.Access == models.DS_ACCESS_DIRECT {
|
||||
if ds.Access == datasources.DS_ACCESS_DIRECT {
|
||||
if ds.BasicAuth {
|
||||
password, err := hs.DataSourcesService.DecryptedBasicAuthPassword(c.Req.Context(), ds)
|
||||
if err != nil {
|
||||
@@ -264,7 +265,7 @@ func (hs *HTTPServer) getFSDataSources(c *models.ReqContext, enabledPlugins Enab
|
||||
dsDTO.WithCredentials = ds.WithCredentials
|
||||
}
|
||||
|
||||
if ds.Type == models.DS_INFLUXDB_08 {
|
||||
if ds.Type == datasources.DS_INFLUXDB_08 {
|
||||
password, err := hs.DataSourcesService.DecryptedPassword(c.Req.Context(), ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -275,7 +276,7 @@ func (hs *HTTPServer) getFSDataSources(c *models.ReqContext, enabledPlugins Enab
|
||||
dsDTO.URL = url + "/db/" + ds.Database
|
||||
}
|
||||
|
||||
if ds.Type == models.DS_INFLUXDB {
|
||||
if ds.Type == datasources.DS_INFLUXDB {
|
||||
password, err := hs.DataSourcesService.DecryptedPassword(c.Req.Context(), ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -287,11 +288,11 @@ func (hs *HTTPServer) getFSDataSources(c *models.ReqContext, enabledPlugins Enab
|
||||
}
|
||||
}
|
||||
|
||||
if (ds.Type == models.DS_INFLUXDB) || (ds.Type == models.DS_ES) {
|
||||
if (ds.Type == datasources.DS_INFLUXDB) || (ds.Type == datasources.DS_ES) {
|
||||
dsDTO.Database = ds.Database
|
||||
}
|
||||
|
||||
if ds.Type == models.DS_PROMETHEUS {
|
||||
if ds.Type == datasources.DS_PROMETHEUS {
|
||||
// add unproxied server URL for link to Prometheus web UI
|
||||
ds.JsonData.Set("directUrl", ds.Url)
|
||||
}
|
||||
|
||||
+3
-2
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/query"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@@ -17,10 +18,10 @@ import (
|
||||
)
|
||||
|
||||
func (hs *HTTPServer) handleQueryMetricsError(err error) *response.NormalResponse {
|
||||
if errors.Is(err, models.ErrDataSourceAccessDenied) {
|
||||
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
|
||||
return response.Error(http.StatusForbidden, "Access denied to data source", err)
|
||||
}
|
||||
if errors.Is(err, models.ErrDataSourceNotFound) {
|
||||
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
||||
return response.Error(http.StatusNotFound, "Data source not found", err)
|
||||
}
|
||||
var badQuery *query.ErrBadQuery
|
||||
|
||||
@@ -8,9 +8,11 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
@@ -51,7 +53,7 @@ func (ts *fakeOAuthTokenService) GetCurrentOAuthToken(context.Context, *models.S
|
||||
return ts.token
|
||||
}
|
||||
|
||||
func (ts *fakeOAuthTokenService) IsOAuthPassThruEnabled(*models.DataSource) bool {
|
||||
func (ts *fakeOAuthTokenService) IsOAuthPassThruEnabled(*datasources.DataSource) bool {
|
||||
return ts.passThruEnabled
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/util/proxyutil"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
@@ -47,7 +49,7 @@ func (hs *HTTPServer) callPluginResource(c *models.ReqContext, pluginID string)
|
||||
}
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) callPluginResourceWithDataSource(c *models.ReqContext, pluginID string, ds *models.DataSource) {
|
||||
func (hs *HTTPServer) callPluginResourceWithDataSource(c *models.ReqContext, pluginID string, ds *datasources.DataSource) {
|
||||
pCtx, found, err := hs.PluginContextProvider.GetWithDataSource(c.Req.Context(), pluginID, c.SignedInUser, ds)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Failed to get plugin settings", err)
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/datasource"
|
||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||
glog "github.com/grafana/grafana/pkg/infra/log"
|
||||
@@ -22,7 +24,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/util/proxyutil"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -31,7 +32,7 @@ var (
|
||||
)
|
||||
|
||||
type DataSourceProxy struct {
|
||||
ds *models.DataSource
|
||||
ds *datasources.DataSource
|
||||
ctx *models.ReqContext
|
||||
targetUrl *url.URL
|
||||
proxyPath string
|
||||
@@ -49,7 +50,7 @@ type httpClient interface {
|
||||
}
|
||||
|
||||
// NewDataSourceProxy creates a new Datasource proxy
|
||||
func NewDataSourceProxy(ds *models.DataSource, pluginRoutes []*plugins.Route, ctx *models.ReqContext,
|
||||
func NewDataSourceProxy(ds *datasources.DataSource, pluginRoutes []*plugins.Route, ctx *models.ReqContext,
|
||||
proxyPath string, cfg *setting.Cfg, clientProvider httpclient.Provider,
|
||||
oAuthTokenService oauthtoken.OAuthTokenService, dsService datasources.DataSourceService,
|
||||
tracer tracing.Tracer) (*DataSourceProxy, error) {
|
||||
@@ -167,7 +168,7 @@ func (proxy *DataSourceProxy) director(req *http.Request) {
|
||||
reqQueryVals := req.URL.Query()
|
||||
|
||||
switch proxy.ds.Type {
|
||||
case models.DS_INFLUXDB_08:
|
||||
case datasources.DS_INFLUXDB_08:
|
||||
password, err := proxy.dataSourcesService.DecryptedPassword(req.Context(), proxy.ds)
|
||||
if err != nil {
|
||||
logger.Error("Error interpolating proxy url", "error", err)
|
||||
@@ -178,7 +179,7 @@ func (proxy *DataSourceProxy) director(req *http.Request) {
|
||||
reqQueryVals.Add("u", proxy.ds.User)
|
||||
reqQueryVals.Add("p", password)
|
||||
req.URL.RawQuery = reqQueryVals.Encode()
|
||||
case models.DS_INFLUXDB:
|
||||
case datasources.DS_INFLUXDB:
|
||||
password, err := proxy.dataSourcesService.DecryptedPassword(req.Context(), proxy.ds)
|
||||
if err != nil {
|
||||
logger.Error("Error interpolating proxy url", "error", err)
|
||||
@@ -266,7 +267,7 @@ func (proxy *DataSourceProxy) validateRequest() error {
|
||||
return errors.New("target URL is not a valid target")
|
||||
}
|
||||
|
||||
if proxy.ds.Type == models.DS_ES {
|
||||
if proxy.ds.Type == datasources.DS_ES {
|
||||
if proxy.ctx.Req.Method == "DELETE" {
|
||||
return errors.New("deletes not allowed on proxied Elasticsearch datasource")
|
||||
}
|
||||
@@ -301,7 +302,7 @@ func (proxy *DataSourceProxy) validateRequest() error {
|
||||
}
|
||||
|
||||
// Trailing validation below this point for routes that were not matched
|
||||
if proxy.ds.Type == models.DS_PROMETHEUS {
|
||||
if proxy.ds.Type == datasources.DS_PROMETHEUS {
|
||||
if proxy.ctx.Req.Method == "DELETE" {
|
||||
return errors.New("non allow-listed DELETEs not allowed on proxied Prometheus datasource")
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/datasource"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||
@@ -20,6 +24,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
datasourceservice "github.com/grafana/grafana/pkg/services/datasources/service"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/oauthtoken"
|
||||
@@ -29,9 +34,6 @@ import (
|
||||
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
@@ -96,7 +98,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
key, err := secretsService.Encrypt(context.Background(), []byte("123"), secrets.WithoutScope())
|
||||
require.NoError(t, err)
|
||||
|
||||
ds := &models.DataSource{
|
||||
ds := &datasources.DataSource{
|
||||
JsonData: simplejson.NewFromAny(map[string]interface{}{
|
||||
"clientId": "asd",
|
||||
"dynamicUrl": "https://dynamic.grafana.com",
|
||||
@@ -249,7 +251,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
key, err := secretsService.Encrypt(context.Background(), []byte("123"), secrets.WithoutScope())
|
||||
require.NoError(t, err)
|
||||
|
||||
ds := &models.DataSource{
|
||||
ds := &datasources.DataSource{
|
||||
JsonData: simplejson.NewFromAny(map[string]interface{}{
|
||||
"clientId": "asd",
|
||||
"tenantId": "mytenantId",
|
||||
@@ -340,7 +342,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
|
||||
t.Run("When proxying graphite", func(t *testing.T) {
|
||||
var routes []*plugins.Route
|
||||
ds := &models.DataSource{Url: "htttp://graphite:8080", Type: models.DS_GRAPHITE}
|
||||
ds := &datasources.DataSource{Url: "htttp://graphite:8080", Type: datasources.DS_GRAPHITE}
|
||||
ctx := &models.ReqContext{}
|
||||
|
||||
secretsStore := kvstore.SetupTestService(t)
|
||||
@@ -360,8 +362,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("When proxying InfluxDB", func(t *testing.T) {
|
||||
ds := &models.DataSource{
|
||||
Type: models.DS_INFLUXDB_08,
|
||||
ds := &datasources.DataSource{
|
||||
Type: datasources.DS_INFLUXDB_08,
|
||||
Url: "http://influxdb:8083",
|
||||
Database: "site",
|
||||
User: "user",
|
||||
@@ -386,8 +388,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
json, err := simplejson.NewJson([]byte(`{"keepCookies": []}`))
|
||||
require.NoError(t, err)
|
||||
|
||||
ds := &models.DataSource{
|
||||
Type: models.DS_GRAPHITE,
|
||||
ds := &datasources.DataSource{
|
||||
Type: datasources.DS_GRAPHITE,
|
||||
Url: "http://graphite:8086",
|
||||
JsonData: json,
|
||||
}
|
||||
@@ -415,8 +417,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
json, err := simplejson.NewJson([]byte(`{"keepCookies": ["JSESSION_ID"]}`))
|
||||
require.NoError(t, err)
|
||||
|
||||
ds := &models.DataSource{
|
||||
Type: models.DS_GRAPHITE,
|
||||
ds := &datasources.DataSource{
|
||||
Type: datasources.DS_GRAPHITE,
|
||||
Url: "http://graphite:8086",
|
||||
JsonData: json,
|
||||
}
|
||||
@@ -441,7 +443,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("When proxying a custom datasource", func(t *testing.T) {
|
||||
ds := &models.DataSource{
|
||||
ds := &datasources.DataSource{
|
||||
Type: "custom-datasource",
|
||||
Url: "http://host/root/",
|
||||
}
|
||||
@@ -466,7 +468,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("When proxying a datasource that has OAuth token pass-through enabled", func(t *testing.T) {
|
||||
ds := &models.DataSource{
|
||||
ds := &datasources.DataSource{
|
||||
Type: "custom-datasource",
|
||||
Url: "http://host/root/",
|
||||
JsonData: simplejson.NewFromAny(map[string]interface{}{
|
||||
@@ -555,18 +557,18 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
|
||||
tests := []*testCase{
|
||||
createAuthTest(t, secretsStore, models.DS_INFLUXDB_08, "http://localhost:9090", authTypePassword, authCheckQuery),
|
||||
createAuthTest(t, secretsStore, models.DS_INFLUXDB_08, "http://localhost:9090", authTypePassword, authCheckQuery),
|
||||
createAuthTest(t, secretsStore, models.DS_INFLUXDB, "http://localhost:9090", authTypePassword, authCheckHeader),
|
||||
createAuthTest(t, secretsStore, models.DS_INFLUXDB, "http://localhost:9090", authTypePassword, authCheckHeader),
|
||||
createAuthTest(t, secretsStore, models.DS_INFLUXDB, "http://localhost:9090", authTypeBasic, authCheckHeader),
|
||||
createAuthTest(t, secretsStore, models.DS_INFLUXDB, "http://localhost:9090", authTypeBasic, authCheckHeader),
|
||||
createAuthTest(t, secretsStore, datasources.DS_INFLUXDB_08, "http://localhost:9090", authTypePassword, authCheckQuery),
|
||||
createAuthTest(t, secretsStore, datasources.DS_INFLUXDB_08, "http://localhost:9090", authTypePassword, authCheckQuery),
|
||||
createAuthTest(t, secretsStore, datasources.DS_INFLUXDB, "http://localhost:9090", authTypePassword, authCheckHeader),
|
||||
createAuthTest(t, secretsStore, datasources.DS_INFLUXDB, "http://localhost:9090", authTypePassword, authCheckHeader),
|
||||
createAuthTest(t, secretsStore, datasources.DS_INFLUXDB, "http://localhost:9090", authTypeBasic, authCheckHeader),
|
||||
createAuthTest(t, secretsStore, datasources.DS_INFLUXDB, "http://localhost:9090", authTypeBasic, authCheckHeader),
|
||||
|
||||
// These two should be enough for any other datasource at the moment. Proxy has special handling
|
||||
// only for Influx, others have the same path and only BasicAuth. Non BasicAuth datasources
|
||||
// do not go through proxy but through TSDB API which is not tested here.
|
||||
createAuthTest(t, secretsStore, models.DS_ES, "http://localhost:9200", authTypeBasic, authCheckHeader),
|
||||
createAuthTest(t, secretsStore, models.DS_ES, "http://localhost:9200", authTypeBasic, authCheckHeader),
|
||||
createAuthTest(t, secretsStore, datasources.DS_ES, "http://localhost:9200", authTypeBasic, authCheckHeader),
|
||||
createAuthTest(t, secretsStore, datasources.DS_ES, "http://localhost:9200", authTypeBasic, authCheckHeader),
|
||||
}
|
||||
for _, test := range tests {
|
||||
runDatasourceAuthTest(t, secretsService, secretsStore, cfg, test)
|
||||
@@ -585,7 +587,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
|
||||
writeCb func(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
setUp := func(t *testing.T, cfgs ...setUpCfg) (*models.ReqContext, *models.DataSource) {
|
||||
setUp := func(t *testing.T, cfgs ...setUpCfg) (*models.ReqContext, *datasources.DataSource) {
|
||||
writeErr = nil
|
||||
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -606,7 +608,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
|
||||
}))
|
||||
t.Cleanup(backend.Close)
|
||||
|
||||
ds := &models.DataSource{Url: backend.URL, Type: models.DS_GRAPHITE}
|
||||
ds := &datasources.DataSource{Url: backend.URL, Type: datasources.DS_GRAPHITE}
|
||||
|
||||
responseWriter := web.NewResponseWriter("GET", httptest.NewRecorder())
|
||||
|
||||
@@ -758,7 +760,7 @@ func TestNewDataSourceProxy_InvalidURL(t *testing.T) {
|
||||
Context: &web.Context{},
|
||||
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR},
|
||||
}
|
||||
ds := models.DataSource{
|
||||
ds := datasources.DataSource{
|
||||
Type: "test",
|
||||
Url: "://host/root",
|
||||
}
|
||||
@@ -778,7 +780,7 @@ func TestNewDataSourceProxy_ProtocolLessURL(t *testing.T) {
|
||||
Context: &web.Context{},
|
||||
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR},
|
||||
}
|
||||
ds := models.DataSource{
|
||||
ds := datasources.DataSource{
|
||||
Type: "test",
|
||||
Url: "127.0.01:5432",
|
||||
}
|
||||
@@ -823,7 +825,7 @@ func TestNewDataSourceProxy_MSSQL(t *testing.T) {
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
cfg := &setting.Cfg{}
|
||||
ds := models.DataSource{
|
||||
ds := datasources.DataSource{
|
||||
Type: "mssql",
|
||||
Url: tc.url,
|
||||
}
|
||||
@@ -849,7 +851,7 @@ func TestNewDataSourceProxy_MSSQL(t *testing.T) {
|
||||
|
||||
// getDatasourceProxiedRequest is a helper for easier setup of tests based on global config and ReqContext.
|
||||
func getDatasourceProxiedRequest(t *testing.T, ctx *models.ReqContext, cfg *setting.Cfg) *http.Request {
|
||||
ds := &models.DataSource{
|
||||
ds := &datasources.DataSource{
|
||||
Type: "custom",
|
||||
Url: "http://host/root/",
|
||||
}
|
||||
@@ -897,7 +899,7 @@ func newFakeHTTPClient(t *testing.T, fakeBody []byte) httpClient {
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
datasource *models.DataSource
|
||||
datasource *datasources.DataSource
|
||||
checkReq func(req *http.Request)
|
||||
}
|
||||
|
||||
@@ -916,7 +918,7 @@ func createAuthTest(t *testing.T, secretsStore kvstore.SecretsKVStore, dsType st
|
||||
base64AuthHeader := "Basic dXNlcjpwYXNzd29yZA=="
|
||||
|
||||
test := &testCase{
|
||||
datasource: &models.DataSource{
|
||||
datasource: &datasources.DataSource{
|
||||
Id: 1,
|
||||
OrgId: 1,
|
||||
Name: fmt.Sprintf("%s,%s,%s,%s", dsType, url, authType, authCheck),
|
||||
@@ -1019,7 +1021,7 @@ func Test_PathCheck(t *testing.T) {
|
||||
secretsStore := kvstore.SetupTestService(t)
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
dsService := datasourceservice.ProvideService(nil, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), acmock.NewMockedPermissionsService())
|
||||
proxy, err := NewDataSourceProxy(&models.DataSource{}, routes, ctx, "b", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
|
||||
proxy, err := NewDataSourceProxy(&datasources.DataSource{}, routes, ctx, "b", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Nil(t, proxy.validateRequest())
|
||||
@@ -1035,6 +1037,6 @@ func (m *mockOAuthTokenService) GetCurrentOAuthToken(ctx context.Context, user *
|
||||
return m.token
|
||||
}
|
||||
|
||||
func (m *mockOAuthTokenService) IsOAuthPassThruEnabled(ds *models.DataSource) bool {
|
||||
func (m *mockOAuthTokenService) IsOAuthPassThruEnabled(ds *datasources.DataSource) bool {
|
||||
return m.oAuthEnabled
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user