grafana / grafana-app-sdk

An SDK for developing apps for grafana using kubernetes-like storage and operators
Apache License 2.0
41 stars 7 forks source link

ServerResponseError does not cast the kubernetes StatusError type #301

Open joeblubaugh opened 1 month ago

joeblubaugh commented 1 month ago

Kubernetes apimachinery has a "StatusError" type.

The app-sdk has an ServerResponseError type that abstracts certain k8s errors, and it uses an interface to match kubernetes api errors and status fields:

type APIServerResponseError interface {
    error
    StatusCode() int
}

However, StatusError has no such method, and instead uses a field to expose the status code:

type StatusError struct {
    ErrStatus metav1.Status
}

...
// metav1.Status
// Status is a return value for calls that don't return other objects.
type Status struct {
    TypeMeta `json:",inline"`
    // Standard list metadata.
    // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
    // +optional
    ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

    // Status of the operation.
    // One of: "Success" or "Failure".
    // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
    // +optional
    Status string `json:"status,omitempty" protobuf:"bytes,2,opt,name=status"`
    // A human-readable description of the status of this operation.
    // +optional
    Message string `json:"message,omitempty" protobuf:"bytes,3,opt,name=message"`
    // A machine-readable description of why this operation is in the
    // "Failure" status. If this value is empty there
    // is no information available. A Reason clarifies an HTTP status
    // code but does not override it.
    // +optional
    Reason StatusReason `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason,casttype=StatusReason"`
    // Extended data associated with the reason.  Each reason may define its
    // own extended details. This field is optional and the data returned
    // is not guaranteed to conform to any schema except that defined by
    // the reason type.
    // +optional
    // +listType=atomic
    Details *StatusDetails `json:"details,omitempty" protobuf:"bytes,5,opt,name=details"`
    // Suggested HTTP return code for this status, 0 if not set.
    // +optional
    Code int32 `json:"code,omitempty" protobuf:"varint,6,opt,name=code"`
}

I've encountered circumstances where TypedStore returns a StatusError instead of a ServerResponseError because this casting code doesn't understand that type:

func (t *TypedStore[T]) Upsert(ctx context.Context, identifier Identifier, obj T) (T, error) {
    resp, err := t.client.Get(ctx, identifier)

    if err != nil {
        var n T
        cast, ok := err.(APIServerResponseError)
        if !ok {
            return n, err
        } else if cast.StatusCode() != http.StatusNotFound {
            return n, err
        }
    }

should ServerResponseError also wrap / adapt StatusError in some way, or should we expect to handle k8s StatusError?