mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 00:32:21 +08:00
Config: Add configuration option to define custom user-facing general error message for certain error types (#70023)
--------- Co-authored-by: Summer Wollin <summer.wollin@grafana.com> Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> Co-authored-by: Arati R. <33031346+suntala@users.noreply.github.com>
This commit is contained in:
@ -882,6 +882,9 @@ level = info
|
|||||||
# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
|
# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
|
||||||
filters =
|
filters =
|
||||||
|
|
||||||
|
# Set the default error message shown to users. This message is displayed instead of sensitive backend errors which should be obfuscated.
|
||||||
|
user_facing_default_error = "please inspect Grafana server log for details"
|
||||||
|
|
||||||
# For "console" mode only
|
# For "console" mode only
|
||||||
[log.console]
|
[log.console]
|
||||||
level =
|
level =
|
||||||
|
@ -842,6 +842,9 @@
|
|||||||
# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
|
# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
|
||||||
;filters =
|
;filters =
|
||||||
|
|
||||||
|
# Set the default error message shown to users. This message is displayed instead of sensitive backend errors which should be obfuscated. Default is the same as the sample value.
|
||||||
|
;user_facing_default_error = "please inspect Grafana server log for details"
|
||||||
|
|
||||||
# For "console" mode only
|
# For "console" mode only
|
||||||
[log.console]
|
[log.console]
|
||||||
;level =
|
;level =
|
||||||
|
@ -1249,6 +1249,10 @@ Options are "debug", "info", "warn", "error", and "critical". Default is `info`.
|
|||||||
Optional settings to set different levels for specific loggers.
|
Optional settings to set different levels for specific loggers.
|
||||||
For example: `filters = sqlstore:debug`
|
For example: `filters = sqlstore:debug`
|
||||||
|
|
||||||
|
### user_facing_default_error
|
||||||
|
|
||||||
|
Use this configuration option to set the default error message shown to users. This message is displayed instead of sensitive backend errors, which should be obfuscated. The default message is `Please inspect the Grafana server log for details.`.
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
## [log.console]
|
## [log.console]
|
||||||
|
@ -32,7 +32,7 @@ func (hs *HTTPServer) AdminReEncryptSecrets(c *contextmodel.ReqContext) response
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !success {
|
if !success {
|
||||||
return response.Error(http.StatusPartialContent, "Something unexpected happened, refer to the server logs for more details", err)
|
return response.Error(http.StatusPartialContent, fmt.Sprintf("Something unexpected happened - %s", hs.Cfg.UserFacingDefaultError), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Respond(http.StatusOK, "Secrets re-encrypted successfully")
|
return response.Respond(http.StatusOK, "Secrets re-encrypted successfully")
|
||||||
@ -45,7 +45,7 @@ func (hs *HTTPServer) AdminRollbackSecrets(c *contextmodel.ReqContext) response.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !success {
|
if !success {
|
||||||
return response.Error(http.StatusPartialContent, "Something unexpected happened, refer to the server logs for more details", err)
|
return response.Error(http.StatusPartialContent, fmt.Sprintf("Something unexpected happened - %s", hs.Cfg.UserFacingDefaultError), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Respond(http.StatusOK, "Secrets rolled back successfully")
|
return response.Respond(http.StatusOK, "Secrets rolled back successfully")
|
||||||
|
@ -153,7 +153,7 @@ func Recovery(cfg *setting.Cfg) web.Middleware {
|
|||||||
|
|
||||||
if ctx != nil && ctx.IsApiRequest() {
|
if ctx != nil && ctx.IsApiRequest() {
|
||||||
resp := make(map[string]interface{})
|
resp := make(map[string]interface{})
|
||||||
resp["message"] = "Internal Server Error - Check the Grafana server logs for the detailed error message."
|
resp["message"] = fmt.Sprintf("Internal Server Error - %s", cfg.UserFacingDefaultError)
|
||||||
|
|
||||||
if data.ErrorMsg != "" {
|
if data.ErrorMsg != "" {
|
||||||
resp["error"] = fmt.Sprintf("%v - %v", data.Title, data.ErrorMsg)
|
resp["error"] = fmt.Sprintf("%v - %v", data.Title, data.ErrorMsg)
|
||||||
|
@ -24,7 +24,7 @@ func TestRecoveryMiddleware(t *testing.T) {
|
|||||||
sc.req.Header.Set("content-type", "application/json")
|
sc.req.Header.Set("content-type", "application/json")
|
||||||
|
|
||||||
assert.Equal(t, 500, sc.resp.Code)
|
assert.Equal(t, 500, sc.resp.Code)
|
||||||
assert.Equal(t, "Internal Server Error - Check the Grafana server logs for the detailed error message.", sc.respJson["message"])
|
assert.Equal(t, "Internal Server Error - test error", sc.respJson["message"])
|
||||||
assert.True(t, strings.HasPrefix(sc.respJson["error"].(string), "Server Error"))
|
assert.True(t, strings.HasPrefix(sc.respJson["error"].(string), "Server Error"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -50,6 +50,7 @@ func recoveryScenario(t *testing.T, desc string, url string, fn scenarioFunc) {
|
|||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
cfg.ErrTemplateName = "error-template"
|
cfg.ErrTemplateName = "error-template"
|
||||||
|
cfg.UserFacingDefaultError = "test error"
|
||||||
sc := &scenarioContext{
|
sc := &scenarioContext{
|
||||||
t: t,
|
t: t,
|
||||||
url: url,
|
url: url,
|
||||||
|
@ -117,7 +117,7 @@ func (s *ServiceImpl) executeConcurrentQueries(ctx context.Context, user *user.S
|
|||||||
} else if theErrString, ok := r.(string); ok {
|
} else if theErrString, ok := r.(string); ok {
|
||||||
err = fmt.Errorf(theErrString)
|
err = fmt.Errorf(theErrString)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("unexpected error, see the server log for details")
|
err = fmt.Errorf("unexpected error - %s", s.cfg.UserFacingDefaultError)
|
||||||
}
|
}
|
||||||
// Due to the panic, there is no valid response for any query for this datasource. Append an error for each one.
|
// Due to the panic, there is no valid response for any query for this datasource. Append an error for each one.
|
||||||
rchan <- buildErrorResponses(err, queries)
|
rchan <- buildErrorResponses(err, queries)
|
||||||
|
@ -537,6 +537,9 @@ type Cfg struct {
|
|||||||
|
|
||||||
CustomResponseHeaders map[string]string
|
CustomResponseHeaders map[string]string
|
||||||
|
|
||||||
|
// This is used to override the general error message shown to users when we want to obfuscate a sensitive backend error
|
||||||
|
UserFacingDefaultError string
|
||||||
|
|
||||||
// DatabaseInstrumentQueries is used to decide if database queries
|
// DatabaseInstrumentQueries is used to decide if database queries
|
||||||
// should be instrumented with metrics, logs and traces.
|
// should be instrumented with metrics, logs and traces.
|
||||||
// This needs to be on the global object since its used in the
|
// This needs to be on the global object since its used in the
|
||||||
@ -1224,6 +1227,9 @@ func (cfg *Cfg) Load(args CommandLineArgs) error {
|
|||||||
databaseSection := iniFile.Section("database")
|
databaseSection := iniFile.Section("database")
|
||||||
cfg.DatabaseInstrumentQueries = databaseSection.Key("instrument_queries").MustBool(false)
|
cfg.DatabaseInstrumentQueries = databaseSection.Key("instrument_queries").MustBool(false)
|
||||||
|
|
||||||
|
logSection := iniFile.Section("log")
|
||||||
|
cfg.UserFacingDefaultError = logSection.Key("user_facing_default_error").MustString("please inspect Grafana server log for details")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,9 +109,11 @@ func newInstanceSettings(cfg *setting.Cfg) datasource.InstanceFactoryFunc {
|
|||||||
RowLimit: cfg.DataProxyRowLimit,
|
RowLimit: cfg.DataProxyRowLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
queryResultTransformer := mssqlQueryResultTransformer{}
|
queryResultTransformer := mssqlQueryResultTransformer{
|
||||||
|
userError: cfg.UserFacingDefaultError,
|
||||||
|
}
|
||||||
|
|
||||||
return sqleng.NewQueryDataHandler(config, &queryResultTransformer, newMssqlMacroEngine(), logger)
|
return sqleng.NewQueryDataHandler(cfg, config, &queryResultTransformer, newMssqlMacroEngine(), logger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,14 +199,16 @@ func generateConnectionString(dsInfo sqleng.DataSourceInfo) (string, error) {
|
|||||||
return connStr, nil
|
return connStr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type mssqlQueryResultTransformer struct{}
|
type mssqlQueryResultTransformer struct {
|
||||||
|
userError string
|
||||||
|
}
|
||||||
|
|
||||||
func (t *mssqlQueryResultTransformer) TransformQueryError(logger log.Logger, err error) error {
|
func (t *mssqlQueryResultTransformer) TransformQueryError(logger log.Logger, err error) error {
|
||||||
// go-mssql overrides source error, so we currently match on string
|
// go-mssql overrides source error, so we currently match on string
|
||||||
// ref https://github.com/denisenkom/go-mssqldb/blob/045585d74f9069afe2e115b6235eb043c8047043/tds.go#L904
|
// ref https://github.com/denisenkom/go-mssqldb/blob/045585d74f9069afe2e115b6235eb043c8047043/tds.go#L904
|
||||||
if strings.HasPrefix(strings.ToLower(err.Error()), "unable to open tcp connection with host") {
|
if strings.HasPrefix(strings.ToLower(err.Error()), "unable to open tcp connection with host") {
|
||||||
logger.Error("Query error", "error", err)
|
logger.Error("Query error", "error", err)
|
||||||
return sqleng.ErrConnectionFailed
|
return sqleng.ErrConnectionFailed.Errorf("failed to connect to server - %s", t.userError)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
|
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ func TestMSSQL(t *testing.T) {
|
|||||||
MetricColumnTypes: []string{"VARCHAR", "CHAR", "NVARCHAR", "NCHAR"},
|
MetricColumnTypes: []string{"VARCHAR", "CHAR", "NVARCHAR", "NCHAR"},
|
||||||
RowLimit: 1000000,
|
RowLimit: 1000000,
|
||||||
}
|
}
|
||||||
endpoint, err := sqleng.NewQueryDataHandler(config, &queryResultTransformer, newMssqlMacroEngine(), logger)
|
endpoint, err := sqleng.NewQueryDataHandler(setting.NewCfg(), config, &queryResultTransformer, newMssqlMacroEngine(), logger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
@ -793,7 +794,7 @@ func TestMSSQL(t *testing.T) {
|
|||||||
MetricColumnTypes: []string{"VARCHAR", "CHAR", "NVARCHAR", "NCHAR"},
|
MetricColumnTypes: []string{"VARCHAR", "CHAR", "NVARCHAR", "NCHAR"},
|
||||||
RowLimit: 1000000,
|
RowLimit: 1000000,
|
||||||
}
|
}
|
||||||
endpoint, err := sqleng.NewQueryDataHandler(config, &queryResultTransformer, newMssqlMacroEngine(), logger)
|
endpoint, err := sqleng.NewQueryDataHandler(setting.NewCfg(), config, &queryResultTransformer, newMssqlMacroEngine(), logger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
query := &backend.QueryDataRequest{
|
query := &backend.QueryDataRequest{
|
||||||
Queries: []backend.DataQuery{
|
Queries: []backend.DataQuery{
|
||||||
@ -1199,7 +1200,7 @@ func TestMSSQL(t *testing.T) {
|
|||||||
RowLimit: 1,
|
RowLimit: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
handler, err := sqleng.NewQueryDataHandler(config, &queryResultTransformer, newMssqlMacroEngine(), logger)
|
handler, err := sqleng.NewQueryDataHandler(setting.NewCfg(), config, &queryResultTransformer, newMssqlMacroEngine(), logger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("When doing a table query that returns 2 rows should limit the result to 1 row", func(t *testing.T) {
|
t.Run("When doing a table query that returns 2 rows should limit the result to 1 row", func(t *testing.T) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package mysql
|
package mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@ -9,6 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,17 +20,22 @@ var restrictedRegExp = regexp.MustCompile(`(?im)([\s]*show[\s]+grants|[\s,]sessi
|
|||||||
type mySQLMacroEngine struct {
|
type mySQLMacroEngine struct {
|
||||||
*sqleng.SQLMacroEngineBase
|
*sqleng.SQLMacroEngineBase
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
userError string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMysqlMacroEngine(logger log.Logger) sqleng.SQLMacroEngine {
|
func newMysqlMacroEngine(logger log.Logger, cfg *setting.Cfg) sqleng.SQLMacroEngine {
|
||||||
return &mySQLMacroEngine{SQLMacroEngineBase: sqleng.NewSQLMacroEngineBase(), logger: logger}
|
return &mySQLMacroEngine{
|
||||||
|
SQLMacroEngineBase: sqleng.NewSQLMacroEngineBase(),
|
||||||
|
logger: logger,
|
||||||
|
userError: cfg.UserFacingDefaultError,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mySQLMacroEngine) Interpolate(query *backend.DataQuery, timeRange backend.TimeRange, sql string) (string, error) {
|
func (m *mySQLMacroEngine) Interpolate(query *backend.DataQuery, timeRange backend.TimeRange, sql string) (string, error) {
|
||||||
matches := restrictedRegExp.FindAllStringSubmatch(sql, 1)
|
matches := restrictedRegExp.FindAllStringSubmatch(sql, 1)
|
||||||
if len(matches) > 0 {
|
if len(matches) > 0 {
|
||||||
m.logger.Error("show grants, session_user(), current_user(), system_user() or user() not allowed in query")
|
m.logger.Error("show grants, session_user(), current_user(), system_user() or user() not allowed in query")
|
||||||
return "", errors.New("invalid query - inspect Grafana server log for details")
|
return "", fmt.Errorf("invalid query - %s", m.userError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle error
|
// TODO: Handle error
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
func TestMacroEngine(t *testing.T) {
|
func TestMacroEngine(t *testing.T) {
|
||||||
engine := &mySQLMacroEngine{
|
engine := &mySQLMacroEngine{
|
||||||
logger: log.New("test"),
|
logger: log.New("test"),
|
||||||
|
userError: "inspect Grafana server log for details",
|
||||||
}
|
}
|
||||||
query := &backend.DataQuery{}
|
query := &backend.DataQuery{}
|
||||||
|
|
||||||
@ -193,7 +195,7 @@ func TestMacroEngine(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMacroEngineConcurrency(t *testing.T) {
|
func TestMacroEngineConcurrency(t *testing.T) {
|
||||||
engine := newMysqlMacroEngine(log.New("test"))
|
engine := newMysqlMacroEngine(log.New("test"), setting.NewCfg())
|
||||||
query1 := backend.DataQuery{
|
query1 := backend.DataQuery{
|
||||||
JSON: []byte{},
|
JSON: []byte{},
|
||||||
}
|
}
|
||||||
|
@ -141,9 +141,11 @@ func newInstanceSettings(cfg *setting.Cfg, httpClientProvider httpclient.Provide
|
|||||||
RowLimit: cfg.DataProxyRowLimit,
|
RowLimit: cfg.DataProxyRowLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
rowTransformer := mysqlQueryResultTransformer{}
|
rowTransformer := mysqlQueryResultTransformer{
|
||||||
|
userError: cfg.UserFacingDefaultError,
|
||||||
|
}
|
||||||
|
|
||||||
return sqleng.NewQueryDataHandler(config, &rowTransformer, newMysqlMacroEngine(logger), logger)
|
return sqleng.NewQueryDataHandler(cfg, config, &rowTransformer, newMysqlMacroEngine(logger, cfg), logger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,6 +186,7 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mysqlQueryResultTransformer struct {
|
type mysqlQueryResultTransformer struct {
|
||||||
|
userError string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *mysqlQueryResultTransformer) TransformQueryError(logger log.Logger, err error) error {
|
func (t *mysqlQueryResultTransformer) TransformQueryError(logger log.Logger, err error) error {
|
||||||
@ -192,15 +195,13 @@ func (t *mysqlQueryResultTransformer) TransformQueryError(logger log.Logger, err
|
|||||||
if driverErr.Number != mysqlerr.ER_PARSE_ERROR && driverErr.Number != mysqlerr.ER_BAD_FIELD_ERROR &&
|
if driverErr.Number != mysqlerr.ER_PARSE_ERROR && driverErr.Number != mysqlerr.ER_BAD_FIELD_ERROR &&
|
||||||
driverErr.Number != mysqlerr.ER_NO_SUCH_TABLE {
|
driverErr.Number != mysqlerr.ER_NO_SUCH_TABLE {
|
||||||
logger.Error("Query error", "error", err)
|
logger.Error("Query error", "error", err)
|
||||||
return errQueryFailed
|
return fmt.Errorf(("query failed - %s"), t.userError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var errQueryFailed = errors.New("query failed - please inspect Grafana server log for details")
|
|
||||||
|
|
||||||
func (t *mysqlQueryResultTransformer) GetConverterList() []sqlutil.StringConverter {
|
func (t *mysqlQueryResultTransformer) GetConverterList() []sqlutil.StringConverter {
|
||||||
// For the MySQL driver , we have these possible data types:
|
// For the MySQL driver , we have these possible data types:
|
||||||
// https://www.w3schools.com/sql/sql_datatypes.asp#:~:text=In%20MySQL%20there%20are%20three,numeric%2C%20and%20date%20and%20time.
|
// https://www.w3schools.com/sql/sql_datatypes.asp#:~:text=In%20MySQL%20there%20are%20three,numeric%2C%20and%20date%20and%20time.
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
|
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ func TestIntegrationMySQL(t *testing.T) {
|
|||||||
|
|
||||||
rowTransformer := mysqlQueryResultTransformer{}
|
rowTransformer := mysqlQueryResultTransformer{}
|
||||||
|
|
||||||
exe, err := sqleng.NewQueryDataHandler(config, &rowTransformer, newMysqlMacroEngine(logger), logger)
|
exe, err := sqleng.NewQueryDataHandler(setting.NewCfg(), config, &rowTransformer, newMysqlMacroEngine(logger, setting.NewCfg()), logger)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -1165,7 +1166,7 @@ func TestIntegrationMySQL(t *testing.T) {
|
|||||||
|
|
||||||
queryResultTransformer := mysqlQueryResultTransformer{}
|
queryResultTransformer := mysqlQueryResultTransformer{}
|
||||||
|
|
||||||
handler, err := sqleng.NewQueryDataHandler(config, &queryResultTransformer, newMysqlMacroEngine(logger), logger)
|
handler, err := sqleng.NewQueryDataHandler(setting.NewCfg(), config, &queryResultTransformer, newMysqlMacroEngine(logger, setting.NewCfg()), logger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("When doing a table query that returns 2 rows should limit the result to 1 row", func(t *testing.T) {
|
t.Run("When doing a table query that returns 2 rows should limit the result to 1 row", func(t *testing.T) {
|
||||||
|
@ -112,7 +112,7 @@ func (s *Service) newInstanceSettings(cfg *setting.Cfg) datasource.InstanceFacto
|
|||||||
|
|
||||||
queryResultTransformer := postgresQueryResultTransformer{}
|
queryResultTransformer := postgresQueryResultTransformer{}
|
||||||
|
|
||||||
handler, err := sqleng.NewQueryDataHandler(config, &queryResultTransformer, newPostgresMacroEngine(dsInfo.JsonData.Timescaledb),
|
handler, err := sqleng.NewQueryDataHandler(cfg, config, &queryResultTransformer, newPostgresMacroEngine(dsInfo.JsonData.Timescaledb),
|
||||||
logger)
|
logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed connecting to Postgres", "err", err)
|
logger.Error("Failed connecting to Postgres", "err", err)
|
||||||
|
@ -225,7 +225,7 @@ func TestIntegrationPostgres(t *testing.T) {
|
|||||||
|
|
||||||
queryResultTransformer := postgresQueryResultTransformer{}
|
queryResultTransformer := postgresQueryResultTransformer{}
|
||||||
|
|
||||||
exe, err := sqleng.NewQueryDataHandler(config, &queryResultTransformer, newPostgresMacroEngine(dsInfo.JsonData.Timescaledb),
|
exe, err := sqleng.NewQueryDataHandler(cfg, config, &queryResultTransformer, newPostgresMacroEngine(dsInfo.JsonData.Timescaledb),
|
||||||
logger)
|
logger)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -1267,7 +1267,7 @@ func TestIntegrationPostgres(t *testing.T) {
|
|||||||
|
|
||||||
queryResultTransformer := postgresQueryResultTransformer{}
|
queryResultTransformer := postgresQueryResultTransformer{}
|
||||||
|
|
||||||
handler, err := sqleng.NewQueryDataHandler(config, &queryResultTransformer, newPostgresMacroEngine(false), logger)
|
handler, err := sqleng.NewQueryDataHandler(setting.NewCfg(), config, &queryResultTransformer, newPostgresMacroEngine(false), logger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("When doing a table query that returns 2 rows should limit the result to 1 row", func(t *testing.T) {
|
t.Run("When doing a table query that returns 2 rows should limit the result to 1 row", func(t *testing.T) {
|
||||||
|
@ -20,7 +20,9 @@ import (
|
|||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||||
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// XormDriverMu is used to allow safe concurrent registering and querying of drivers in xorm
|
// XormDriverMu is used to allow safe concurrent registering and querying of drivers in xorm
|
||||||
@ -29,7 +31,7 @@ var XormDriverMu sync.RWMutex
|
|||||||
// MetaKeyExecutedQueryString is the key where the executed query should get stored
|
// MetaKeyExecutedQueryString is the key where the executed query should get stored
|
||||||
const MetaKeyExecutedQueryString = "executedQueryString"
|
const MetaKeyExecutedQueryString = "executedQueryString"
|
||||||
|
|
||||||
var ErrConnectionFailed = errors.New("failed to connect to server - please inspect Grafana server log for details")
|
var ErrConnectionFailed = errutil.NewBase(errutil.StatusInternal, "sqleng.connectionError")
|
||||||
|
|
||||||
// SQLMacroEngine interpolates macros into sql. It takes in the Query to have access to query context and
|
// SQLMacroEngine interpolates macros into sql. It takes in the Query to have access to query context and
|
||||||
// timeRange to be able to generate queries that use from and to.
|
// timeRange to be able to generate queries that use from and to.
|
||||||
@ -100,6 +102,7 @@ type DataPluginConfiguration struct {
|
|||||||
MetricColumnTypes []string
|
MetricColumnTypes []string
|
||||||
RowLimit int64
|
RowLimit int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataSourceHandler struct {
|
type DataSourceHandler struct {
|
||||||
macroEngine SQLMacroEngine
|
macroEngine SQLMacroEngine
|
||||||
queryResultTransformer SqlQueryResultTransformer
|
queryResultTransformer SqlQueryResultTransformer
|
||||||
@ -109,6 +112,7 @@ type DataSourceHandler struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
dsInfo DataSourceInfo
|
dsInfo DataSourceInfo
|
||||||
rowLimit int64
|
rowLimit int64
|
||||||
|
userError string
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryJson struct {
|
type QueryJson struct {
|
||||||
@ -128,13 +132,13 @@ func (e *DataSourceHandler) TransformQueryError(logger log.Logger, err error) er
|
|||||||
var opErr *net.OpError
|
var opErr *net.OpError
|
||||||
if errors.As(err, &opErr) {
|
if errors.As(err, &opErr) {
|
||||||
logger.Error("Query error", "err", err)
|
logger.Error("Query error", "err", err)
|
||||||
return ErrConnectionFailed
|
return ErrConnectionFailed.Errorf("failed to connect to server - %s", e.userError)
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.queryResultTransformer.TransformQueryError(logger, err)
|
return e.queryResultTransformer.TransformQueryError(logger, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewQueryDataHandler(config DataPluginConfiguration, queryResultTransformer SqlQueryResultTransformer,
|
func NewQueryDataHandler(cfg *setting.Cfg, config DataPluginConfiguration, queryResultTransformer SqlQueryResultTransformer,
|
||||||
macroEngine SQLMacroEngine, log log.Logger) (*DataSourceHandler, error) {
|
macroEngine SQLMacroEngine, log log.Logger) (*DataSourceHandler, error) {
|
||||||
log.Debug("Creating engine...")
|
log.Debug("Creating engine...")
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -148,6 +152,7 @@ func NewQueryDataHandler(config DataPluginConfiguration, queryResultTransformer
|
|||||||
log: log,
|
log: log,
|
||||||
dsInfo: config.DSInfo,
|
dsInfo: config.DSInfo,
|
||||||
rowLimit: config.RowLimit,
|
rowLimit: config.RowLimit,
|
||||||
|
userError: cfg.UserFacingDefaultError,
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(config.TimeColumnNames) > 0 {
|
if len(config.TimeColumnNames) > 0 {
|
||||||
@ -242,7 +247,7 @@ func (e *DataSourceHandler) executeQuery(query backend.DataQuery, wg *sync.WaitG
|
|||||||
} else if theErrString, ok := r.(string); ok {
|
} else if theErrString, ok := r.(string); ok {
|
||||||
queryResult.dataResponse.Error = fmt.Errorf(theErrString)
|
queryResult.dataResponse.Error = fmt.Errorf(theErrString)
|
||||||
} else {
|
} else {
|
||||||
queryResult.dataResponse.Error = fmt.Errorf("unexpected error, see the server log for details")
|
queryResult.dataResponse.Error = fmt.Errorf("unexpected error - %s", e.userError)
|
||||||
}
|
}
|
||||||
ch <- queryResult
|
ch <- queryResult
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user