From bd42a3edb05518d43ce37d972968fdd77c90223d Mon Sep 17 00:00:00 2001 From: Ivana Huckova Date: Thu, 3 Oct 2024 18:04:14 +0200 Subject: [PATCH 1/7] IsDownstreamHttpError --- backend/error_source.go | 77 +++++++++++++++++++++++++++++++++++++++ backend/request_status.go | 19 ---------- 2 files changed, 77 insertions(+), 19 deletions(-) diff --git a/backend/error_source.go b/backend/error_source.go index 8c157cf30..194b0deda 100644 --- a/backend/error_source.go +++ b/backend/error_source.go @@ -4,7 +4,13 @@ import ( "context" "errors" "fmt" + "net" "net/http" + "os" + "syscall" + + grpccodes "google.golang.org/grpc/codes" + grpcstatus "google.golang.org/grpc/status" ) // ErrorSource type defines the source of the error @@ -143,3 +149,74 @@ func WithErrorSource(ctx context.Context, s ErrorSource) error { func WithDownstreamErrorSource(ctx context.Context) error { return WithErrorSource(ctx, ErrorSourceDownstream) } + +func IsDownstreamHttpError(err error) bool { + e := errorWithSourceImpl{ + source: ErrorSourceDownstream, + } + if errors.Is(err, e) { + return true + } + + type errorWithSource interface { + ErrorSource() ErrorSource + } + + // nolint:errorlint + if errWithSource, ok := err.(errorWithSource); ok && errWithSource.ErrorSource() == ErrorSourceDownstream { + return true + } + + // Check if the error is a HTTP timeout error or a context cancelled error + if isHTTPTimeoutError(err){ + return true + } + + if isCancelledError(err) { + return true + } + + if isConnectionResetOrRefusedError(err) { + return true + } + + if isDnsNotFoundError(err) { + return true + } + + return false +} + + +func isCancelledError(err error) bool { + return errors.Is(err, context.Canceled) || grpcstatus.Code(err) == grpccodes.Canceled +} + +func isHTTPTimeoutError(err error) bool { + var netErr net.Error + if errors.As(err, &netErr) && netErr.Timeout() { + return true + } + + return errors.Is(err, os.ErrDeadlineExceeded) // replacement for os.IsTimeout(err) +} + +func isConnectionResetOrRefusedError(err error) bool { + var netErr *net.OpError + if errors.As(err, &netErr) { + if sysErr, ok := netErr.Err.(*os.SyscallError); ok { + return sysErr.Err == syscall.ECONNRESET || sysErr.Err == syscall.ECONNREFUSED + } + } + + return false +} + +func isDnsNotFoundError(err error) bool { + var dnsError *net.DNSError + if errors.As(err, &dnsError) && dnsError.IsNotFound { + return true + } + + return false +} \ No newline at end of file diff --git a/backend/request_status.go b/backend/request_status.go index 36280295e..8ca967d1c 100644 --- a/backend/request_status.go +++ b/backend/request_status.go @@ -2,14 +2,8 @@ package backend import ( "context" - "errors" - "net" - "os" "strings" - grpccodes "google.golang.org/grpc/codes" - grpcstatus "google.golang.org/grpc/status" - "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" ) @@ -103,16 +97,3 @@ func RequestStatusFromProtoQueryDataResponse(res *pluginv2.QueryDataResponse, er return status } - -func isCancelledError(err error) bool { - return errors.Is(err, context.Canceled) || grpcstatus.Code(err) == grpccodes.Canceled -} - -func isHTTPTimeoutError(err error) bool { - var netErr net.Error - if errors.As(err, &netErr) && netErr.Timeout() { - return true - } - - return errors.Is(err, os.ErrDeadlineExceeded) // replacement for os.IsTimeout(err) -} From 24e7104c8b88955824e3b165e0d487bc17aa221c Mon Sep 17 00:00:00 2001 From: Ivana Huckova Date: Thu, 3 Oct 2024 18:19:46 +0200 Subject: [PATCH 2/7] POC --- backend/httpclient/error_source_middleware.go | 24 +++++++++++++++++++ backend/httpclient/http_client.go | 1 + 2 files changed, 25 insertions(+) create mode 100644 backend/httpclient/error_source_middleware.go diff --git a/backend/httpclient/error_source_middleware.go b/backend/httpclient/error_source_middleware.go new file mode 100644 index 000000000..f5e364aec --- /dev/null +++ b/backend/httpclient/error_source_middleware.go @@ -0,0 +1,24 @@ +package httpclient + +import ( + "net/http" + + // this is throwing cicular dependency error - will need to refactor it + // if we want to use it + "github.com/grafana/grafana-plugin-sdk-go/backend" +) + +const ErrorSourceMiddlewareName = "ErrorSource" + +func ErrorSourceMiddleware() Middleware { + return NamedMiddlewareFunc(ResponseLimitMiddlewareName, func(_ Options, next http.RoundTripper) http.RoundTripper { + return RoundTripperFunc(func(req *http.Request) (*http.Response, error) { + res, err := next.RoundTrip(req) + if err != nil && backend.IsDownstreamHttpError(err) { + return res, backend.DownstreamError(err) + } + + return res, nil + }) + }) +} \ No newline at end of file diff --git a/backend/httpclient/http_client.go b/backend/httpclient/http_client.go index 40a4d39d4..0d88f72e8 100644 --- a/backend/httpclient/http_client.go +++ b/backend/httpclient/http_client.go @@ -210,6 +210,7 @@ func DefaultMiddlewares() []Middleware { BasicAuthenticationMiddleware(), CustomHeadersMiddleware(), ContextualMiddleware(), + ErrorSourceMiddleware(), } } From a8ebeb9349a370d36c1e9a5418beae32d04244a3 Mon Sep 17 00:00:00 2001 From: Ivana Huckova Date: Tue, 8 Oct 2024 14:19:59 +0200 Subject: [PATCH 3/7] Add error source http error handling --- backend/error_source.go | 9 ++ backend/httpclient/error_source_middleware.go | 125 +++++++++++++++++- .../errorsource/error_source_middleware.go | 8 +- 3 files changed, 129 insertions(+), 13 deletions(-) diff --git a/backend/error_source.go b/backend/error_source.go index 194b0deda..f5662f328 100644 --- a/backend/error_source.go +++ b/backend/error_source.go @@ -9,6 +9,8 @@ import ( "os" "syscall" + "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + grpccodes "google.golang.org/grpc/codes" grpcstatus "google.golang.org/grpc/status" ) @@ -31,6 +33,13 @@ func (es ErrorSource) IsValid() bool { return es == ErrorSourceDownstream || es == ErrorSourcePlugin } +func ErrorSourceFromHttpError(err error) ErrorSource { + if httpclient.IsDownstreamHttpError(err) { + return ErrorSourceDownstream + } + return ErrorSourcePlugin +} + // ErrorSourceFromStatus returns an [ErrorSource] based on provided HTTP status code. func ErrorSourceFromHTTPStatus(statusCode int) ErrorSource { switch statusCode { diff --git a/backend/httpclient/error_source_middleware.go b/backend/httpclient/error_source_middleware.go index f5e364aec..1d47d4446 100644 --- a/backend/httpclient/error_source_middleware.go +++ b/backend/httpclient/error_source_middleware.go @@ -1,11 +1,16 @@ package httpclient import ( + "context" + "errors" + "fmt" + "net" "net/http" + "os" + "syscall" - // this is throwing cicular dependency error - will need to refactor it - // if we want to use it - "github.com/grafana/grafana-plugin-sdk-go/backend" + grpccodes "google.golang.org/grpc/codes" + grpcstatus "google.golang.org/grpc/status" ) const ErrorSourceMiddlewareName = "ErrorSource" @@ -14,11 +19,119 @@ func ErrorSourceMiddleware() Middleware { return NamedMiddlewareFunc(ResponseLimitMiddlewareName, func(_ Options, next http.RoundTripper) http.RoundTripper { return RoundTripperFunc(func(req *http.Request) (*http.Response, error) { res, err := next.RoundTrip(req) - if err != nil && backend.IsDownstreamHttpError(err) { - return res, backend.DownstreamError(err) + if err != nil && IsDownstreamHttpError(err) { + return res, DownstreamError(err) } - return res, nil + return res, err }) }) +} + + +type ErrorSource string +const ( + ErrorSourcePlugin ErrorSource = "plugin" + ErrorSourceDownstream ErrorSource = "downstream" +) + +type errorWithSourceImpl struct { + source ErrorSource + err error +} + +func IsDownstreamHttpError(err error) bool { + e := errorWithSourceImpl{ + source: ErrorSourceDownstream, + } + if errors.Is(err, e) { + return true + } + + // nolint:errorlint + if errWithSource, ok := err.(errorWithSourceImpl); ok && errWithSource.ErrorSource() == ErrorSourceDownstream { + return true + } + + // Check if the error is a HTTP timeout error or a context cancelled error + if isHTTPTimeoutError(err){ + return true + } + + if isCancelledError(err) { + return true + } + + if isConnectionResetOrRefusedError(err) { + return true + } + + if isDnsNotFoundError(err) { + return true + } + + return false +} + + +func isCancelledError(err error) bool { + return errors.Is(err, context.Canceled) || grpcstatus.Code(err) == grpccodes.Canceled +} + +func isHTTPTimeoutError(err error) bool { + var netErr net.Error + if errors.As(err, &netErr) && netErr.Timeout() { + return true + } + + return errors.Is(err, os.ErrDeadlineExceeded) // replacement for os.IsTimeout(err) +} + +func isConnectionResetOrRefusedError(err error) bool { + var netErr *net.OpError + if errors.As(err, &netErr) { + if sysErr, ok := netErr.Err.(*os.SyscallError); ok { + return sysErr.Err == syscall.ECONNRESET || sysErr.Err == syscall.ECONNREFUSED + } + } + + return false +} + +func isDnsNotFoundError(err error) bool { + var dnsError *net.DNSError + if errors.As(err, &dnsError) && dnsError.IsNotFound { + return true + } + + return false +} + + +func (e errorWithSourceImpl) ErrorSource() ErrorSource { + return e.source +} + +func (e errorWithSourceImpl) Error() string { + return fmt.Errorf("%s error: %w", e.source, e.err).Error() +} + +// Implements the interface used by [errors.Is]. +func (e errorWithSourceImpl) Is(err error) bool { + if errWithSource, ok := err.(errorWithSourceImpl); ok { + return errWithSource.ErrorSource() == e.source + } + + return false +} + +func (e errorWithSourceImpl) Unwrap() error { + return e.err +} + +func DownstreamError(err error) error { + return errorWithSourceImpl{ + source: ErrorSourceDownstream, + err: err, + } } \ No newline at end of file diff --git a/experimental/errorsource/error_source_middleware.go b/experimental/errorsource/error_source_middleware.go index 95ff79b10..9656141f7 100644 --- a/experimental/errorsource/error_source_middleware.go +++ b/experimental/errorsource/error_source_middleware.go @@ -2,9 +2,7 @@ package errorsource import ( "errors" - "net" "net/http" - "syscall" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" @@ -26,11 +24,7 @@ func RoundTripper(_ httpclient.Options, next http.RoundTripper) http.RoundTrippe } return res, Error{source: errorSource, err: err} } - if errors.Is(err, syscall.ECONNREFUSED) { - return res, Error{source: backend.ErrorSourceDownstream, err: err} - } - var dnsError *net.DNSError - if errors.As(err, &dnsError) && dnsError.IsNotFound { + if httpclient.IsDownstreamHttpError(err) { return res, Error{source: backend.ErrorSourceDownstream, err: err} } return res, err From 68b101950cbaceea26e54809d8b216efc9d0dc08 Mon Sep 17 00:00:00 2001 From: Ivana Huckova Date: Tue, 8 Oct 2024 14:57:18 +0200 Subject: [PATCH 4/7] Fix lint errors --- backend/error_source.go | 63 +------------------ backend/httpclient/error_source_middleware.go | 27 ++++---- .../errorsource/error_source_middleware.go | 2 +- 3 files changed, 16 insertions(+), 76 deletions(-) diff --git a/backend/error_source.go b/backend/error_source.go index f5662f328..2ee66bae2 100644 --- a/backend/error_source.go +++ b/backend/error_source.go @@ -7,7 +7,6 @@ import ( "net" "net/http" "os" - "syscall" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" @@ -33,8 +32,8 @@ func (es ErrorSource) IsValid() bool { return es == ErrorSourceDownstream || es == ErrorSourcePlugin } -func ErrorSourceFromHttpError(err error) ErrorSource { - if httpclient.IsDownstreamHttpError(err) { +func ErrorSourceFromHTTPError(err error) ErrorSource { + if httpclient.IsDownstreamHTTPError(err) { return ErrorSourceDownstream } return ErrorSourcePlugin @@ -159,44 +158,6 @@ func WithDownstreamErrorSource(ctx context.Context) error { return WithErrorSource(ctx, ErrorSourceDownstream) } -func IsDownstreamHttpError(err error) bool { - e := errorWithSourceImpl{ - source: ErrorSourceDownstream, - } - if errors.Is(err, e) { - return true - } - - type errorWithSource interface { - ErrorSource() ErrorSource - } - - // nolint:errorlint - if errWithSource, ok := err.(errorWithSource); ok && errWithSource.ErrorSource() == ErrorSourceDownstream { - return true - } - - // Check if the error is a HTTP timeout error or a context cancelled error - if isHTTPTimeoutError(err){ - return true - } - - if isCancelledError(err) { - return true - } - - if isConnectionResetOrRefusedError(err) { - return true - } - - if isDnsNotFoundError(err) { - return true - } - - return false -} - - func isCancelledError(err error) bool { return errors.Is(err, context.Canceled) || grpcstatus.Code(err) == grpccodes.Canceled } @@ -209,23 +170,3 @@ func isHTTPTimeoutError(err error) bool { return errors.Is(err, os.ErrDeadlineExceeded) // replacement for os.IsTimeout(err) } - -func isConnectionResetOrRefusedError(err error) bool { - var netErr *net.OpError - if errors.As(err, &netErr) { - if sysErr, ok := netErr.Err.(*os.SyscallError); ok { - return sysErr.Err == syscall.ECONNRESET || sysErr.Err == syscall.ECONNREFUSED - } - } - - return false -} - -func isDnsNotFoundError(err error) bool { - var dnsError *net.DNSError - if errors.As(err, &dnsError) && dnsError.IsNotFound { - return true - } - - return false -} \ No newline at end of file diff --git a/backend/httpclient/error_source_middleware.go b/backend/httpclient/error_source_middleware.go index 1d47d4446..41f853127 100644 --- a/backend/httpclient/error_source_middleware.go +++ b/backend/httpclient/error_source_middleware.go @@ -19,8 +19,8 @@ func ErrorSourceMiddleware() Middleware { return NamedMiddlewareFunc(ResponseLimitMiddlewareName, func(_ Options, next http.RoundTripper) http.RoundTripper { return RoundTripperFunc(func(req *http.Request) (*http.Response, error) { res, err := next.RoundTrip(req) - if err != nil && IsDownstreamHttpError(err) { - return res, DownstreamError(err) + if err != nil && IsDownstreamHTTPError(err) { + return res, DownstreamError(err) } return res, err @@ -28,10 +28,10 @@ func ErrorSourceMiddleware() Middleware { }) } - type ErrorSource string + const ( - ErrorSourcePlugin ErrorSource = "plugin" + ErrorSourcePlugin ErrorSource = "plugin" ErrorSourceDownstream ErrorSource = "downstream" ) @@ -40,7 +40,7 @@ type errorWithSourceImpl struct { err error } -func IsDownstreamHttpError(err error) bool { +func IsDownstreamHTTPError(err error) bool { e := errorWithSourceImpl{ source: ErrorSourceDownstream, } @@ -54,11 +54,11 @@ func IsDownstreamHttpError(err error) bool { } // Check if the error is a HTTP timeout error or a context cancelled error - if isHTTPTimeoutError(err){ + if isHTTPTimeoutError(err) { return true } - if isCancelledError(err) { + if isCancelledError(err) { return true } @@ -66,14 +66,13 @@ func IsDownstreamHttpError(err error) bool { return true } - if isDnsNotFoundError(err) { + if isDNSNotFoundError(err) { return true } return false } - func isCancelledError(err error) bool { return errors.Is(err, context.Canceled) || grpcstatus.Code(err) == grpccodes.Canceled } @@ -90,15 +89,16 @@ func isHTTPTimeoutError(err error) bool { func isConnectionResetOrRefusedError(err error) bool { var netErr *net.OpError if errors.As(err, &netErr) { - if sysErr, ok := netErr.Err.(*os.SyscallError); ok { - return sysErr.Err == syscall.ECONNRESET || sysErr.Err == syscall.ECONNREFUSED + var sysErr *os.SyscallError + if errors.As(netErr.Err, &sysErr) { + return errors.Is(sysErr.Err, syscall.ECONNRESET) || errors.Is(sysErr.Err, syscall.ECONNREFUSED) } } return false } -func isDnsNotFoundError(err error) bool { +func isDNSNotFoundError(err error) bool { var dnsError *net.DNSError if errors.As(err, &dnsError) && dnsError.IsNotFound { return true @@ -107,7 +107,6 @@ func isDnsNotFoundError(err error) bool { return false } - func (e errorWithSourceImpl) ErrorSource() ErrorSource { return e.source } @@ -134,4 +133,4 @@ func DownstreamError(err error) error { source: ErrorSourceDownstream, err: err, } -} \ No newline at end of file +} diff --git a/experimental/errorsource/error_source_middleware.go b/experimental/errorsource/error_source_middleware.go index 9656141f7..db9ef5dfa 100644 --- a/experimental/errorsource/error_source_middleware.go +++ b/experimental/errorsource/error_source_middleware.go @@ -24,7 +24,7 @@ func RoundTripper(_ httpclient.Options, next http.RoundTripper) http.RoundTrippe } return res, Error{source: errorSource, err: err} } - if httpclient.IsDownstreamHttpError(err) { + if httpclient.IsDownstreamHTTPError(err) { return res, Error{source: backend.ErrorSourceDownstream, err: err} } return res, err From 3a194ca4af93e39530490599748a7b74e188d6b4 Mon Sep 17 00:00:00 2001 From: Ivana Huckova Date: Tue, 8 Oct 2024 15:05:12 +0200 Subject: [PATCH 5/7] Fix test --- backend/httpclient/error_source_middleware.go | 2 +- backend/httpclient/http_client_test.go | 3 ++- backend/httpclient/provider_test.go | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/httpclient/error_source_middleware.go b/backend/httpclient/error_source_middleware.go index 41f853127..0d7a383ad 100644 --- a/backend/httpclient/error_source_middleware.go +++ b/backend/httpclient/error_source_middleware.go @@ -16,7 +16,7 @@ import ( const ErrorSourceMiddlewareName = "ErrorSource" func ErrorSourceMiddleware() Middleware { - return NamedMiddlewareFunc(ResponseLimitMiddlewareName, func(_ Options, next http.RoundTripper) http.RoundTripper { + return NamedMiddlewareFunc(ErrorSourceMiddlewareName, func(_ Options, next http.RoundTripper) http.RoundTripper { return RoundTripperFunc(func(req *http.Request) (*http.Response, error) { res, err := next.RoundTrip(req) if err != nil && IsDownstreamHTTPError(err) { diff --git a/backend/httpclient/http_client_test.go b/backend/httpclient/http_client_test.go index 570fe0833..e8875d180 100644 --- a/backend/httpclient/http_client_test.go +++ b/backend/httpclient/http_client_test.go @@ -55,11 +55,12 @@ func TestNewClient(t *testing.T) { require.NoError(t, err) require.NotNil(t, client) - require.Len(t, usedMiddlewares, 4) + require.Len(t, usedMiddlewares, 5) require.Equal(t, TracingMiddlewareName, usedMiddlewares[0].(MiddlewareName).MiddlewareName()) require.Equal(t, BasicAuthenticationMiddlewareName, usedMiddlewares[1].(MiddlewareName).MiddlewareName()) require.Equal(t, CustomHeadersMiddlewareName, usedMiddlewares[2].(MiddlewareName).MiddlewareName()) require.Equal(t, ContextualMiddlewareName, usedMiddlewares[3].(MiddlewareName).MiddlewareName()) + require.Equal(t, ErrorSourceMiddlewareName, usedMiddlewares[4].(MiddlewareName).MiddlewareName()) }) t.Run("New() with opts middleware should return expected http.Client", func(t *testing.T) { diff --git a/backend/httpclient/provider_test.go b/backend/httpclient/provider_test.go index b5331321a..8d5984fb1 100644 --- a/backend/httpclient/provider_test.go +++ b/backend/httpclient/provider_test.go @@ -24,7 +24,7 @@ func TestProvider(t *testing.T) { client, err := ctx.provider.New() require.NoError(t, err) require.NotNil(t, client) - require.Len(t, ctx.usedMiddlewares, 4) + require.Len(t, ctx.usedMiddlewares, 5) require.Equal(t, TracingMiddlewareName, ctx.usedMiddlewares[0].(MiddlewareName).MiddlewareName()) require.Equal(t, BasicAuthenticationMiddlewareName, ctx.usedMiddlewares[1].(MiddlewareName).MiddlewareName()) require.Equal(t, CustomHeadersMiddlewareName, ctx.usedMiddlewares[2].(MiddlewareName).MiddlewareName()) @@ -36,7 +36,7 @@ func TestProvider(t *testing.T) { transport, err := ctx.provider.GetTransport() require.NoError(t, err) require.NotNil(t, transport) - require.Len(t, ctx.usedMiddlewares, 4) + require.Len(t, ctx.usedMiddlewares, 5) require.Equal(t, TracingMiddlewareName, ctx.usedMiddlewares[0].(MiddlewareName).MiddlewareName()) require.Equal(t, BasicAuthenticationMiddlewareName, ctx.usedMiddlewares[1].(MiddlewareName).MiddlewareName()) require.Equal(t, CustomHeadersMiddlewareName, ctx.usedMiddlewares[2].(MiddlewareName).MiddlewareName()) @@ -81,7 +81,7 @@ func TestProvider(t *testing.T) { require.Equal(t, DefaultTimeoutOptions.Timeout, client.Timeout) t.Run("Should use configured middlewares and implement MiddlewareName", func(t *testing.T) { - require.Len(t, pCtx.usedMiddlewares, 7) + require.Len(t, pCtx.usedMiddlewares, 8) require.Equal(t, "mw1", pCtx.usedMiddlewares[0].(MiddlewareName).MiddlewareName()) require.Equal(t, "mw2", pCtx.usedMiddlewares[1].(MiddlewareName).MiddlewareName()) require.Equal(t, "mw3", pCtx.usedMiddlewares[2].(MiddlewareName).MiddlewareName()) From 187adf1674eee90d8ad3c769df1c58cf71ae1354 Mon Sep 17 00:00:00 2001 From: Ivana Huckova Date: Tue, 8 Oct 2024 15:31:16 +0200 Subject: [PATCH 6/7] Update --- backend/error_source.go | 18 ------------------ backend/request_status.go | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/backend/error_source.go b/backend/error_source.go index 2ee66bae2..526334f40 100644 --- a/backend/error_source.go +++ b/backend/error_source.go @@ -4,14 +4,9 @@ import ( "context" "errors" "fmt" - "net" "net/http" - "os" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" - - grpccodes "google.golang.org/grpc/codes" - grpcstatus "google.golang.org/grpc/status" ) // ErrorSource type defines the source of the error @@ -157,16 +152,3 @@ func WithErrorSource(ctx context.Context, s ErrorSource) error { func WithDownstreamErrorSource(ctx context.Context) error { return WithErrorSource(ctx, ErrorSourceDownstream) } - -func isCancelledError(err error) bool { - return errors.Is(err, context.Canceled) || grpcstatus.Code(err) == grpccodes.Canceled -} - -func isHTTPTimeoutError(err error) bool { - var netErr net.Error - if errors.As(err, &netErr) && netErr.Timeout() { - return true - } - - return errors.Is(err, os.ErrDeadlineExceeded) // replacement for os.IsTimeout(err) -} diff --git a/backend/request_status.go b/backend/request_status.go index 8ca967d1c..7a819d597 100644 --- a/backend/request_status.go +++ b/backend/request_status.go @@ -2,9 +2,15 @@ package backend import ( "context" + "errors" + "net" + "os" "strings" "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" + + grpccodes "google.golang.org/grpc/codes" + grpcstatus "google.golang.org/grpc/status" ) type RequestStatus int @@ -97,3 +103,16 @@ func RequestStatusFromProtoQueryDataResponse(res *pluginv2.QueryDataResponse, er return status } + +func isCancelledError(err error) bool { + return errors.Is(err, context.Canceled) || grpcstatus.Code(err) == grpccodes.Canceled +} + +func isHTTPTimeoutError(err error) bool { + var netErr net.Error + if errors.As(err, &netErr) && netErr.Timeout() { + return true + } + + return errors.Is(err, os.ErrDeadlineExceeded) // replacement for os.IsTimeout(err) +} From 2bdd0bed15cf57a87b1e7878d00d78b989a2eae9 Mon Sep 17 00:00:00 2001 From: Ivana Huckova Date: Tue, 8 Oct 2024 15:32:04 +0200 Subject: [PATCH 7/7] Update --- backend/request_status.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/request_status.go b/backend/request_status.go index 7a819d597..36280295e 100644 --- a/backend/request_status.go +++ b/backend/request_status.go @@ -7,10 +7,10 @@ import ( "os" "strings" - "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" - grpccodes "google.golang.org/grpc/codes" grpcstatus "google.golang.org/grpc/status" + + "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" ) type RequestStatus int