Postgres: Datasource health check error message improvements #96906 (#96990)

* updated postgres health check error messages

* addressed review comments / added tests
This commit is contained in:
Sriram
2024-11-27 13:14:44 +00:00
committed by GitHub
parent 6e36dfb74e
commit 16c78f6a98
2 changed files with 112 additions and 3 deletions

View File

@ -3,17 +3,22 @@ package sqleng
import (
"context"
"encoding/json"
"errors"
"fmt"
"net"
"strings"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/lib/pq"
)
func (e *DataSourceHandler) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
err := e.Ping()
if err != nil {
logCheckHealthError(ctx, e.dsInfo, err, e.log)
if req.PluginContext.User.Role == "Admin" {
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: err.Error()}, nil
if strings.EqualFold(req.PluginContext.User.Role, "Admin") {
return ErrToHealthCheckResult(err)
}
errResponse := &backend.CheckHealthResult{
Status: backend.HealthStatusError,
@ -24,7 +29,51 @@ func (e *DataSourceHandler) CheckHealth(ctx context.Context, req *backend.CheckH
return &backend.CheckHealthResult{Status: backend.HealthStatusOk, Message: "Database Connection OK"}, nil
}
func logCheckHealthError(ctx context.Context, dsInfo DataSourceInfo, err error, logger log.Logger) {
// ErrToHealthCheckResult converts error into user friendly health check message
// This should be called with non nil error. If the err parameter is empty, we will send Internal Server Error
func ErrToHealthCheckResult(err error) (*backend.CheckHealthResult, error) {
if err == nil {
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: "Internal Server Error"}, nil
}
res := &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: err.Error()}
details := map[string]string{
"verboseMessage": err.Error(),
"errorDetailsLink": "https://grafana.com/docs/grafana/latest/datasources/postgres",
}
var opErr *net.OpError
if errors.As(err, &opErr) {
res.Message = "Network error: Failed to connect to the server"
if opErr != nil && opErr.Err != nil {
res.Message += fmt.Sprintf(". Error message: %s", opErr.Err.Error())
}
}
if errors.Is(err, pq.ErrSSLNotSupported) {
res.Message = "SSL error: Failed to connect to the server"
}
if strings.HasPrefix(err.Error(), "pq") {
res.Message = "Database error: Failed to connect to the postgres server"
if unwrappedErr := errors.Unwrap(err); unwrappedErr != nil {
details["verboseMessage"] = unwrappedErr.Error()
}
}
var pqErr *pq.Error
if errors.As(err, &pqErr) {
if pqErr != nil {
if pqErr.Code != "" {
res.Message += fmt.Sprintf(". Postgres error code: %s", pqErr.Code.Name())
}
details["verboseMessage"] = pqErr.Message
}
}
detailBytes, marshalErr := json.Marshal(details)
if marshalErr != nil {
return res, nil
}
res.JSONDetails = detailBytes
return res, nil
}
func logCheckHealthError(_ context.Context, dsInfo DataSourceInfo, err error, logger log.Logger) {
configSummary := map[string]any{
"config_url_length": len(dsInfo.URL),
"config_user_length": len(dsInfo.User),