forked from TrueCloudLab/frostfs-sdk-go
commit
d7a12a4846
8 changed files with 123 additions and 126 deletions
|
@ -31,14 +31,13 @@ import (
|
|||
// Each method which produces a NeoFS API call may return a server response.
|
||||
// Status responses are returned in the result structure, and can be cast
|
||||
// to built-in error instance (or in the returned error if the client is
|
||||
// configured accordingly). Certain statuses can be checked using `apistatus`
|
||||
// and standard `errors` packages. Note that package provides some helper
|
||||
// functions to work with status returns (e.g. IsErrContainerNotFound).
|
||||
// configured accordingly). Certain statuses can be checked using [apistatus]
|
||||
// and standard [errors] packages.
|
||||
// All possible responses are documented in methods, however, some may be
|
||||
// returned from all of them (pay attention to the presence of the pointer sign):
|
||||
// - *apistatus.ServerInternal on internal server error;
|
||||
// - *apistatus.NodeUnderMaintenance if a server is under maintenance;
|
||||
// - *apistatus.SuccessDefaultV2 on default success.
|
||||
// - *[apistatus.ServerInternal] on internal server error;
|
||||
// - *[apistatus.NodeUnderMaintenance] if a server is under maintenance;
|
||||
// - *[apistatus.SuccessDefaultV2] on default success.
|
||||
//
|
||||
// Client MUST NOT be copied by value: use pointer to Client instead.
|
||||
//
|
||||
|
|
|
@ -1,99 +1,9 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
|
||||
)
|
||||
|
||||
// unwraps err using errors.Unwrap and returns the result.
|
||||
func unwrapErr(err error) error {
|
||||
for e := errors.Unwrap(err); e != nil; e = errors.Unwrap(err) {
|
||||
err = e
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// IsErrContainerNotFound checks if err corresponds to NeoFS status
|
||||
// return corresponding to missing container. Supports wrapped errors.
|
||||
func IsErrContainerNotFound(err error) bool {
|
||||
switch unwrapErr(err).(type) {
|
||||
default:
|
||||
return false
|
||||
case
|
||||
apistatus.ContainerNotFound,
|
||||
*apistatus.ContainerNotFound:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// IsErrEACLNotFound checks if err corresponds to NeoFS status
|
||||
// return corresponding to missing eACL table. Supports wrapped errors.
|
||||
func IsErrEACLNotFound(err error) bool {
|
||||
switch unwrapErr(err).(type) {
|
||||
default:
|
||||
return false
|
||||
case
|
||||
apistatus.EACLNotFound,
|
||||
*apistatus.EACLNotFound:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// IsErrObjectNotFound checks if err corresponds to NeoFS status
|
||||
// return corresponding to missing object. Supports wrapped errors.
|
||||
func IsErrObjectNotFound(err error) bool {
|
||||
switch unwrapErr(err).(type) {
|
||||
default:
|
||||
return false
|
||||
case
|
||||
apistatus.ObjectNotFound,
|
||||
*apistatus.ObjectNotFound:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// IsErrObjectAlreadyRemoved checks if err corresponds to NeoFS status
|
||||
// return corresponding to already removed object. Supports wrapped errors.
|
||||
func IsErrObjectAlreadyRemoved(err error) bool {
|
||||
switch unwrapErr(err).(type) {
|
||||
default:
|
||||
return false
|
||||
case
|
||||
apistatus.ObjectAlreadyRemoved,
|
||||
*apistatus.ObjectAlreadyRemoved:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// IsErrSessionExpired checks if err corresponds to NeoFS status return
|
||||
// corresponding to expired session. Supports wrapped errors.
|
||||
func IsErrSessionExpired(err error) bool {
|
||||
switch unwrapErr(err).(type) {
|
||||
default:
|
||||
return false
|
||||
case
|
||||
apistatus.SessionTokenExpired,
|
||||
*apistatus.SessionTokenExpired:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// IsErrSessionNotFound checks if err corresponds to NeoFS status return
|
||||
// corresponding to missing session. Supports wrapped errors.
|
||||
func IsErrSessionNotFound(err error) bool {
|
||||
switch unwrapErr(err).(type) {
|
||||
default:
|
||||
return false
|
||||
case
|
||||
apistatus.SessionTokenNotFound,
|
||||
*apistatus.SessionTokenNotFound:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// returns error describing missing field with the given name.
|
||||
func newErrMissingResponseField(name string) error {
|
||||
return fmt.Errorf("missing %s field in the response", name)
|
||||
|
|
|
@ -4,65 +4,68 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-sdk-go/client"
|
||||
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestErrors(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
check func(error) bool
|
||||
errs []error
|
||||
errs []error
|
||||
errVariable error
|
||||
}{
|
||||
{
|
||||
check: client.IsErrContainerNotFound,
|
||||
errs: []error{
|
||||
apistatus.ContainerNotFound{},
|
||||
new(apistatus.ContainerNotFound),
|
||||
},
|
||||
errVariable: apistatus.ErrContainerNotFound,
|
||||
},
|
||||
{
|
||||
check: client.IsErrEACLNotFound,
|
||||
errs: []error{
|
||||
apistatus.EACLNotFound{},
|
||||
new(apistatus.EACLNotFound),
|
||||
},
|
||||
errVariable: apistatus.ErrEACLNotFound,
|
||||
},
|
||||
{
|
||||
check: client.IsErrObjectNotFound,
|
||||
errs: []error{
|
||||
apistatus.ObjectNotFound{},
|
||||
new(apistatus.ObjectNotFound),
|
||||
},
|
||||
errVariable: apistatus.ErrObjectNotFound,
|
||||
},
|
||||
{
|
||||
check: client.IsErrObjectAlreadyRemoved,
|
||||
errs: []error{
|
||||
apistatus.ObjectAlreadyRemoved{},
|
||||
new(apistatus.ObjectAlreadyRemoved),
|
||||
},
|
||||
errVariable: apistatus.ErrObjectAlreadyRemoved,
|
||||
},
|
||||
{
|
||||
check: client.IsErrSessionExpired,
|
||||
errs: []error{
|
||||
apistatus.SessionTokenExpired{},
|
||||
new(apistatus.SessionTokenExpired),
|
||||
},
|
||||
errVariable: apistatus.ErrSessionTokenExpired,
|
||||
}, {
|
||||
check: client.IsErrSessionNotFound,
|
||||
errs: []error{
|
||||
apistatus.SessionTokenNotFound{},
|
||||
new(apistatus.SessionTokenNotFound),
|
||||
},
|
||||
errVariable: apistatus.ErrSessionTokenNotFound,
|
||||
},
|
||||
} {
|
||||
require.NotEmpty(t, tc.errs)
|
||||
require.NotNil(t, tc.errVariable)
|
||||
|
||||
for i := range tc.errs {
|
||||
require.True(t, tc.check(tc.errs[i]), tc.errs[i])
|
||||
require.True(t, tc.check(fmt.Errorf("top-level context: :%w",
|
||||
fmt.Errorf("inner context: %w", tc.errs[i])),
|
||||
), tc.errs[i])
|
||||
require.ErrorIs(t, tc.errs[i], tc.errVariable)
|
||||
|
||||
wrapped := fmt.Errorf("some message %w", tc.errs[i])
|
||||
require.ErrorIs(t, wrapped, tc.errVariable)
|
||||
|
||||
wrappedTwice := fmt.Errorf("another message %w", wrapped)
|
||||
require.ErrorIs(t, wrappedTwice, tc.errVariable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
// ServerInternal describes failure statuses related to internal server errors.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
//
|
||||
// The status is purely informative, the client should not go into details of the error except for debugging needs.
|
||||
type ServerInternal struct {
|
||||
|
@ -57,7 +57,7 @@ func WriteInternalServerErr(x *ServerInternal, err error) {
|
|||
}
|
||||
|
||||
// WrongMagicNumber describes failure status related to incorrect network magic.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type WrongMagicNumber struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ func (x WrongMagicNumber) CorrectMagic() (magic uint64, ok int8) {
|
|||
}
|
||||
|
||||
// SignatureVerification describes failure status related to signature verification.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type SignatureVerification struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ func (x SignatureVerification) Message() string {
|
|||
}
|
||||
|
||||
// NodeUnderMaintenance describes failure status for nodes being under maintenance.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type NodeUnderMaintenance struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
|
|
@ -5,8 +5,17 @@ import (
|
|||
"github.com/nspcc-dev/neofs-api-go/v2/status"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrEACLNotFound is an instance of EACLNotFound error status. It's expected to be used for [errors.Is]
|
||||
// and MUST NOT be changed.
|
||||
ErrEACLNotFound EACLNotFound
|
||||
// ErrContainerNotFound is an instance of ContainerNotFound error status. It's expected to be used for [errors.Is]
|
||||
// and MUST NOT be changed.
|
||||
ErrContainerNotFound ContainerNotFound
|
||||
)
|
||||
|
||||
// ContainerNotFound describes status of the failure because of the missing container.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type ContainerNotFound struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
@ -25,6 +34,16 @@ func (x ContainerNotFound) Error() string {
|
|||
)
|
||||
}
|
||||
|
||||
// Is implements interface for correct checking current error type with [errors.Is].
|
||||
func (x ContainerNotFound) Is(target error) bool {
|
||||
switch target.(type) {
|
||||
default:
|
||||
return false
|
||||
case ContainerNotFound, *ContainerNotFound:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// implements local interface defined in FromStatusV2 func.
|
||||
func (x *ContainerNotFound) fromStatusV2(st *status.Status) {
|
||||
x.v2 = *st
|
||||
|
@ -44,7 +63,7 @@ func (x ContainerNotFound) ToStatusV2() *status.Status {
|
|||
|
||||
// EACLNotFound describes status of the failure because of the missing eACL
|
||||
// table.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type EACLNotFound struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
@ -63,6 +82,16 @@ func (x EACLNotFound) Error() string {
|
|||
)
|
||||
}
|
||||
|
||||
// Is implements interface for correct checking current error type with [errors.Is].
|
||||
func (x EACLNotFound) Is(target error) bool {
|
||||
switch target.(type) {
|
||||
default:
|
||||
return false
|
||||
case EACLNotFound, *EACLNotFound:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// implements local interface defined in FromStatusV2 func.
|
||||
func (x *EACLNotFound) fromStatusV2(st *status.Status) {
|
||||
x.v2 = *st
|
||||
|
|
|
@ -5,8 +5,17 @@ import (
|
|||
"github.com/nspcc-dev/neofs-api-go/v2/status"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrObjectAlreadyRemoved is an instance of ObjectAlreadyRemoved error status. It's expected to be used for [errors.Is]
|
||||
// and MUST NOT be changed.
|
||||
ErrObjectAlreadyRemoved ObjectAlreadyRemoved
|
||||
// ErrObjectNotFound is an instance of ObjectNotFound error status. It's expected to be used for [errors.Is]
|
||||
// and MUST NOT be changed.
|
||||
ErrObjectNotFound ObjectNotFound
|
||||
)
|
||||
|
||||
// ObjectLocked describes status of the failure because of the locked object.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type ObjectLocked struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
@ -43,7 +52,7 @@ func (x ObjectLocked) ToStatusV2() *status.Status {
|
|||
}
|
||||
|
||||
// LockNonRegularObject describes status returned on locking the non-regular object.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type LockNonRegularObject struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
@ -80,7 +89,7 @@ func (x LockNonRegularObject) ToStatusV2() *status.Status {
|
|||
}
|
||||
|
||||
// ObjectAccessDenied describes status of the failure because of the access control violation.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type ObjectAccessDenied struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
@ -128,7 +137,7 @@ func (x ObjectAccessDenied) Reason() string {
|
|||
}
|
||||
|
||||
// ObjectNotFound describes status of the failure because of the missing object.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type ObjectNotFound struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
@ -147,6 +156,16 @@ func (x ObjectNotFound) Error() string {
|
|||
)
|
||||
}
|
||||
|
||||
// Is implements interface for correct checking current error type with [errors.Is].
|
||||
func (x ObjectNotFound) Is(target error) bool {
|
||||
switch target.(type) {
|
||||
default:
|
||||
return false
|
||||
case ObjectNotFound, *ObjectNotFound:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// implements local interface defined in FromStatusV2 func.
|
||||
func (x *ObjectNotFound) fromStatusV2(st *status.Status) {
|
||||
x.v2 = *st
|
||||
|
@ -184,6 +203,16 @@ func (x ObjectAlreadyRemoved) Error() string {
|
|||
)
|
||||
}
|
||||
|
||||
// Is implements interface for correct checking current error type with [errors.Is].
|
||||
func (x ObjectAlreadyRemoved) Is(target error) bool {
|
||||
switch target.(type) {
|
||||
default:
|
||||
return false
|
||||
case ObjectAlreadyRemoved, *ObjectAlreadyRemoved:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// implements local interface defined in FromStatusV2 func.
|
||||
func (x *ObjectAlreadyRemoved) fromStatusV2(st *status.Status) {
|
||||
x.v2 = *st
|
||||
|
@ -203,7 +232,7 @@ func (x ObjectAlreadyRemoved) ToStatusV2() *status.Status {
|
|||
|
||||
// ObjectOutOfRange describes status of the failure because of the incorrect
|
||||
// provided object ranges.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type ObjectOutOfRange struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
|
|
@ -5,8 +5,17 @@ import (
|
|||
"github.com/nspcc-dev/neofs-api-go/v2/status"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrSessionTokenNotFound is an instance of SessionTokenNotFound error status. It's expected to be used for [errors.Is]
|
||||
// and MUST NOT be changed.
|
||||
ErrSessionTokenNotFound SessionTokenNotFound
|
||||
// ErrSessionTokenExpired is an instance of SessionTokenExpired error status. It's expected to be used for [errors.Is]
|
||||
// and MUST NOT be changed.
|
||||
ErrSessionTokenExpired SessionTokenExpired
|
||||
)
|
||||
|
||||
// SessionTokenNotFound describes status of the failure because of the missing session token.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type SessionTokenNotFound struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
@ -25,6 +34,16 @@ func (x SessionTokenNotFound) Error() string {
|
|||
)
|
||||
}
|
||||
|
||||
// Is implements interface for correct checking current error type with [errors.Is].
|
||||
func (x SessionTokenNotFound) Is(target error) bool {
|
||||
switch target.(type) {
|
||||
default:
|
||||
return false
|
||||
case SessionTokenNotFound, *SessionTokenNotFound:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// implements local interface defined in FromStatusV2 func.
|
||||
func (x *SessionTokenNotFound) fromStatusV2(st *status.Status) {
|
||||
x.v2 = *st
|
||||
|
@ -43,7 +62,7 @@ func (x SessionTokenNotFound) ToStatusV2() *status.Status {
|
|||
}
|
||||
|
||||
// SessionTokenExpired describes status of the failure because of the expired session token.
|
||||
// Instances provide Status and StatusV2 interfaces.
|
||||
// Instances provide [Status], [StatusV2] and error interfaces.
|
||||
type SessionTokenExpired struct {
|
||||
v2 status.Status
|
||||
}
|
||||
|
@ -62,6 +81,16 @@ func (x SessionTokenExpired) Error() string {
|
|||
)
|
||||
}
|
||||
|
||||
// Is implements interface for correct checking current error type with [errors.Is].
|
||||
func (x SessionTokenExpired) Is(target error) bool {
|
||||
switch target.(type) {
|
||||
default:
|
||||
return false
|
||||
case SessionTokenExpired, *SessionTokenExpired:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// implements local interface defined in FromStatusV2 func.
|
||||
func (x *SessionTokenExpired) fromStatusV2(st *status.Status) {
|
||||
x.v2 = *st
|
||||
|
|
|
@ -1527,8 +1527,6 @@ type resCreateSession struct {
|
|||
// Each method which produces a NeoFS API call may return an error.
|
||||
// Status of underlying server response is casted to built-in error instance.
|
||||
// Certain statuses can be checked using `sdkClient` and standard `errors` packages.
|
||||
// Note that package provides some helper functions to work with status returns
|
||||
// (e.g. sdkClient.IsErrContainerNotFound, sdkClient.IsErrObjectNotFound).
|
||||
//
|
||||
// See pool package overview to get some examples.
|
||||
type Pool struct {
|
||||
|
@ -1860,7 +1858,7 @@ func (p *Pool) checkSessionTokenErr(err error, address string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if sdkClient.IsErrSessionNotFound(err) || sdkClient.IsErrSessionExpired(err) {
|
||||
if errors.Is(err, apistatus.ErrSessionTokenNotFound) || errors.Is(err, apistatus.ErrSessionTokenExpired) {
|
||||
p.cache.DeleteByPrefix(address)
|
||||
return true
|
||||
}
|
||||
|
@ -2458,7 +2456,7 @@ func waitForContainerRemoved(ctx context.Context, cli client, cnrID *cid.ID, wai
|
|||
|
||||
return waitFor(ctx, waitParams, func(ctx context.Context) bool {
|
||||
_, err := cli.containerGet(ctx, prm)
|
||||
return sdkClient.IsErrContainerNotFound(err)
|
||||
return errors.Is(err, apistatus.ErrContainerNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue