mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 23:52:19 +08:00
144 lines
3.7 KiB
Go
144 lines
3.7 KiB
Go
package resource
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
|
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
|
grpcstatus "google.golang.org/grpc/status"
|
|
|
|
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
|
"github.com/grafana/grafana/pkg/util/scheduler"
|
|
)
|
|
|
|
// Package-level errors.
|
|
var (
|
|
ErrOptimisticLockingFailed = errors.New("optimistic locking failed")
|
|
ErrNotImplementedYet = errors.New("not implemented yet")
|
|
)
|
|
|
|
var (
|
|
ErrResourceAlreadyExists error = &apierrors.StatusError{
|
|
ErrStatus: metav1.Status{
|
|
Status: metav1.StatusFailure,
|
|
Reason: metav1.StatusReasonAlreadyExists,
|
|
Message: "the resource already exists",
|
|
Code: http.StatusConflict,
|
|
},
|
|
}
|
|
)
|
|
|
|
func NewBadRequestError(msg string) *resourcepb.ErrorResult {
|
|
return &resourcepb.ErrorResult{
|
|
Message: msg,
|
|
Code: http.StatusBadRequest,
|
|
Reason: string(metav1.StatusReasonBadRequest),
|
|
}
|
|
}
|
|
|
|
func NewNotFoundError(key *resourcepb.ResourceKey) *resourcepb.ErrorResult {
|
|
return &resourcepb.ErrorResult{
|
|
Code: http.StatusNotFound,
|
|
Details: &resourcepb.ErrorDetails{
|
|
Group: key.Group,
|
|
Kind: key.Resource, // yup, resource as kind same is true in apierrors.NewNotFound()
|
|
Name: key.Name,
|
|
},
|
|
}
|
|
}
|
|
|
|
func NewTooManyRequestsError(msg string) *resourcepb.ErrorResult {
|
|
return &resourcepb.ErrorResult{
|
|
Message: msg,
|
|
Code: http.StatusTooManyRequests,
|
|
Reason: string(metav1.StatusReasonTooManyRequests),
|
|
}
|
|
}
|
|
|
|
// Convert golang errors to status result errors that can be returned to a client
|
|
func AsErrorResult(err error) *resourcepb.ErrorResult {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
var apistatus apierrors.APIStatus
|
|
if errors.As(err, &apistatus) {
|
|
s := apistatus.Status()
|
|
res := &resourcepb.ErrorResult{
|
|
Message: s.Message,
|
|
Reason: string(s.Reason),
|
|
Code: s.Code,
|
|
}
|
|
if s.Details != nil {
|
|
res.Details = &resourcepb.ErrorDetails{
|
|
Group: s.Details.Group,
|
|
Kind: s.Details.Kind,
|
|
Name: s.Details.Name,
|
|
Uid: string(s.Details.UID),
|
|
RetryAfterSeconds: s.Details.RetryAfterSeconds,
|
|
}
|
|
for _, c := range s.Details.Causes {
|
|
res.Details.Causes = append(res.Details.Causes, &resourcepb.ErrorCause{
|
|
Reason: string(c.Type),
|
|
Message: c.Message,
|
|
Field: c.Field,
|
|
})
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
code := 500
|
|
|
|
st, ok := grpcstatus.FromError(err)
|
|
if ok {
|
|
code = runtime.HTTPStatusFromCode(st.Code())
|
|
}
|
|
|
|
return &resourcepb.ErrorResult{
|
|
Message: err.Error(),
|
|
Code: int32(code),
|
|
}
|
|
}
|
|
|
|
func GetError(res *resourcepb.ErrorResult) error {
|
|
if res == nil {
|
|
return nil
|
|
}
|
|
|
|
status := &apierrors.StatusError{ErrStatus: metav1.Status{
|
|
Status: metav1.StatusFailure,
|
|
Code: res.Code,
|
|
Reason: metav1.StatusReason(res.Reason),
|
|
Message: res.Message,
|
|
}}
|
|
if res.Details != nil {
|
|
status.ErrStatus.Details = &metav1.StatusDetails{
|
|
Group: res.Details.Group,
|
|
Kind: res.Details.Kind,
|
|
Name: res.Details.Name,
|
|
UID: types.UID(res.Details.Uid),
|
|
RetryAfterSeconds: res.Details.RetryAfterSeconds,
|
|
}
|
|
for _, c := range res.Details.Causes {
|
|
status.ErrStatus.Details.Causes = append(status.ErrStatus.Details.Causes, metav1.StatusCause{
|
|
Type: metav1.CauseType(c.Reason),
|
|
Message: c.Message,
|
|
Field: c.Field,
|
|
})
|
|
}
|
|
}
|
|
return status
|
|
}
|
|
|
|
func HandleQueueError[T any](err error, makeResp func(*resourcepb.ErrorResult) *T) (*T, error) {
|
|
if errors.Is(err, scheduler.ErrTenantQueueFull) {
|
|
return makeResp(NewTooManyRequestsError("tenant queue is full, please try again later")), nil
|
|
}
|
|
return makeResp(AsErrorResult(err)), nil
|
|
}
|