Skip to content

Commit

Permalink
Merge pull request etcd-io#5222 from gyuho/error_interface
Browse files Browse the repository at this point in the history
rpctypes: error interface
  • Loading branch information
gyuho committed Apr 29, 2016
2 parents 8e099ab + ec1fdd3 commit 3396805
Show file tree
Hide file tree
Showing 17 changed files with 215 additions and 120 deletions.
17 changes: 9 additions & 8 deletions clientv3/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"strings"

"github.com/coreos/etcd/auth/authpb"
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
"golang.org/x/net/context"
"google.golang.org/grpc"
Expand Down Expand Up @@ -87,37 +88,37 @@ func NewAuth(c *Client) Auth {

func (auth *auth) AuthEnable(ctx context.Context) (*AuthEnableResponse, error) {
resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{})
return (*AuthEnableResponse)(resp), err
return (*AuthEnableResponse)(resp), rpctypes.Error(err)
}

func (auth *auth) Authenticate(ctx context.Context, name string, password string) (*AuthenticateResponse, error) {
resp, err := auth.remote.Authenticate(ctx, &pb.AuthenticateRequest{Name: name, Password: password})
return (*AuthenticateResponse)(resp), err
return (*AuthenticateResponse)(resp), rpctypes.Error(err)
}

func (auth *auth) UserAdd(ctx context.Context, name string, password string) (*AuthUserAddResponse, error) {
resp, err := auth.remote.UserAdd(ctx, &pb.AuthUserAddRequest{Name: name, Password: password})
return (*AuthUserAddResponse)(resp), err
return (*AuthUserAddResponse)(resp), rpctypes.Error(err)
}

func (auth *auth) UserDelete(ctx context.Context, name string) (*AuthUserDeleteResponse, error) {
resp, err := auth.remote.UserDelete(ctx, &pb.AuthUserDeleteRequest{Name: name})
return (*AuthUserDeleteResponse)(resp), err
return (*AuthUserDeleteResponse)(resp), rpctypes.Error(err)
}

func (auth *auth) UserChangePassword(ctx context.Context, name string, password string) (*AuthUserChangePasswordResponse, error) {
resp, err := auth.remote.UserChangePassword(ctx, &pb.AuthUserChangePasswordRequest{Name: name, Password: password})
return (*AuthUserChangePasswordResponse)(resp), err
return (*AuthUserChangePasswordResponse)(resp), rpctypes.Error(err)
}

func (auth *auth) UserGrant(ctx context.Context, user string, role string) (*AuthUserGrantResponse, error) {
resp, err := auth.remote.UserGrant(ctx, &pb.AuthUserGrantRequest{User: user, Role: role})
return (*AuthUserGrantResponse)(resp), err
return (*AuthUserGrantResponse)(resp), rpctypes.Error(err)
}

func (auth *auth) RoleAdd(ctx context.Context, name string) (*AuthRoleAddResponse, error) {
resp, err := auth.remote.RoleAdd(ctx, &pb.AuthRoleAddRequest{Name: name})
return (*AuthRoleAddResponse)(resp), err
return (*AuthRoleAddResponse)(resp), rpctypes.Error(err)
}

func (auth *auth) RoleGrant(ctx context.Context, name string, key string, permType PermissionType) (*AuthRoleGrantResponse, error) {
Expand All @@ -126,7 +127,7 @@ func (auth *auth) RoleGrant(ctx context.Context, name string, key string, permTy
PermType: authpb.Permission_Type(permType),
}
resp, err := auth.remote.RoleGrant(ctx, &pb.AuthRoleGrantRequest{Name: name, Perm: perm})
return (*AuthRoleGrantResponse)(resp), err
return (*AuthRoleGrantResponse)(resp), rpctypes.Error(err)
}

func StrToPermissionType(s string) (PermissionType, error) {
Expand Down
2 changes: 1 addition & 1 deletion clientv3/integration/kv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ func TestKVCompact(t *testing.T) {
}
err = kv.Compact(ctx, 7)
if err == nil || err != rpctypes.ErrCompacted {
t.Fatalf("error got %v, want %v", err, rpctypes.ErrFutureRev)
t.Fatalf("error got %v, want %v", err, rpctypes.ErrCompacted)
}

wcli := clus.RandClient()
Expand Down
8 changes: 3 additions & 5 deletions clientv3/integration/lease_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ import (
"github.com/coreos/etcd/integration"
"github.com/coreos/etcd/pkg/testutil"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)

func TestLeastNotFoundError(t *testing.T) {
func TestLeaseNotFoundError(t *testing.T) {
defer testutil.AfterTest(t)

clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
Expand Down Expand Up @@ -113,8 +111,8 @@ func TestLeaseKeepAliveOnce(t *testing.T) {
}

_, err = lapi.KeepAliveOnce(context.Background(), clientv3.LeaseID(0))
if grpc.Code(err) != codes.NotFound {
t.Errorf("invalid error returned %v", err)
if err != rpctypes.ErrLeaseNotFound {
t.Errorf("expected %v, got %v", rpctypes.ErrLeaseNotFound, err)
}
}

Expand Down
2 changes: 1 addition & 1 deletion clientv3/integration/watch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ func TestWatchCompactRevision(t *testing.T) {
t.Fatalf("expected wresp, but got closed channel")
}
if wresp.Err() != rpctypes.ErrCompacted {
t.Fatalf("wresp.Err() expected ErrCompacteed, but got %v", wresp.Err())
t.Fatalf("wresp.Err() expected %v, but got %v", rpctypes.ErrCompacted, wresp.Err())
}

// ensure the channel is closed
Expand Down
18 changes: 9 additions & 9 deletions etcdserver/api/v3rpc/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,33 +125,33 @@ func (s *kvServer) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.Co

func checkRangeRequest(r *pb.RangeRequest) error {
if len(r.Key) == 0 {
return rpctypes.ErrEmptyKey
return rpctypes.ErrGRPCEmptyKey
}
return nil
}

func checkPutRequest(r *pb.PutRequest) error {
if len(r.Key) == 0 {
return rpctypes.ErrEmptyKey
return rpctypes.ErrGRPCEmptyKey
}
return nil
}

func checkDeleteRequest(r *pb.DeleteRangeRequest) error {
if len(r.Key) == 0 {
return rpctypes.ErrEmptyKey
return rpctypes.ErrGRPCEmptyKey
}
return nil
}

func checkTxnRequest(r *pb.TxnRequest) error {
if len(r.Compare) > MaxOpsPerTxn || len(r.Success) > MaxOpsPerTxn || len(r.Failure) > MaxOpsPerTxn {
return rpctypes.ErrTooManyOps
return rpctypes.ErrGRPCTooManyOps
}

for _, c := range r.Compare {
if len(c.Key) == 0 {
return rpctypes.ErrEmptyKey
return rpctypes.ErrGRPCEmptyKey
}
}

Expand All @@ -172,7 +172,7 @@ func checkTxnRequest(r *pb.TxnRequest) error {
return checkRequestDupKeys(r.Failure)
}

// checkRequestDupKeys gives rpctypes.ErrDuplicateKey if the same key is modified twice
// checkRequestDupKeys gives rpctypes.ErrGRPCDuplicateKey if the same key is modified twice
func checkRequestDupKeys(reqs []*pb.RequestUnion) error {
// check put overlap
keys := make(map[string]struct{})
Expand All @@ -186,7 +186,7 @@ func checkRequestDupKeys(reqs []*pb.RequestUnion) error {
continue
}
if _, ok := keys[string(preq.Key)]; ok {
return rpctypes.ErrDuplicateKey
return rpctypes.ErrGRPCDuplicateKey
}
keys[string(preq.Key)] = struct{}{}
}
Expand Down Expand Up @@ -215,14 +215,14 @@ func checkRequestDupKeys(reqs []*pb.RequestUnion) error {
}
if dreq.RangeEnd == nil {
if _, found := keys[string(dreq.Key)]; found {
return rpctypes.ErrDuplicateKey
return rpctypes.ErrGRPCDuplicateKey
}
} else {
lo := sort.SearchStrings(sortedKeys, string(dreq.Key))
hi := sort.SearchStrings(sortedKeys, string(dreq.RangeEnd))
if lo != hi {
// element between lo and hi => overlap
return rpctypes.ErrDuplicateKey
return rpctypes.ErrGRPCDuplicateKey
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions etcdserver/api/v3rpc/lease.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func NewLeaseServer(s *etcdserver.EtcdServer) pb.LeaseServer {
func (ls *LeaseServer) LeaseGrant(ctx context.Context, cr *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
resp, err := ls.le.LeaseGrant(ctx, cr)
if err == lease.ErrLeaseExists {
return nil, rpctypes.ErrLeaseExist
return nil, rpctypes.ErrGRPCLeaseExist
}
if err != nil {
return nil, err
Expand All @@ -48,7 +48,7 @@ func (ls *LeaseServer) LeaseGrant(ctx context.Context, cr *pb.LeaseGrantRequest)
func (ls *LeaseServer) LeaseRevoke(ctx context.Context, rr *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
resp, err := ls.le.LeaseRevoke(ctx, rr)
if err != nil {
return nil, rpctypes.ErrLeaseNotFound
return nil, rpctypes.ErrGRPCLeaseNotFound
}
ls.hdr.fill(resp.Header)
return resp, nil
Expand Down
12 changes: 6 additions & 6 deletions etcdserver/api/v3rpc/member.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ func NewClusterServer(s *etcdserver.EtcdServer) *ClusterServer {
func (cs *ClusterServer) MemberAdd(ctx context.Context, r *pb.MemberAddRequest) (*pb.MemberAddResponse, error) {
urls, err := types.NewURLs(r.PeerURLs)
if err != nil {
return nil, rpctypes.ErrMemberBadURLs
return nil, rpctypes.ErrGRPCMemberBadURLs
}

now := time.Now()
m := membership.NewMember("", urls, "", &now)
err = cs.server.AddMember(ctx, *m)
switch {
case err == membership.ErrIDExists:
return nil, rpctypes.ErrMemberExist
return nil, rpctypes.ErrGRPCMemberExist
case err == membership.ErrPeerURLexists:
return nil, rpctypes.ErrPeerURLExist
return nil, rpctypes.ErrGRPCPeerURLExist
case err != nil:
return nil, grpc.Errorf(codes.Internal, err.Error())
}
Expand All @@ -72,7 +72,7 @@ func (cs *ClusterServer) MemberRemove(ctx context.Context, r *pb.MemberRemoveReq
case err == membership.ErrIDRemoved:
fallthrough
case err == membership.ErrIDNotFound:
return nil, rpctypes.ErrMemberNotFound
return nil, rpctypes.ErrGRPCMemberNotFound
case err != nil:
return nil, grpc.Errorf(codes.Internal, err.Error())
}
Expand All @@ -88,9 +88,9 @@ func (cs *ClusterServer) MemberUpdate(ctx context.Context, r *pb.MemberUpdateReq
err := cs.server.UpdateMember(ctx, m)
switch {
case err == membership.ErrPeerURLexists:
return nil, rpctypes.ErrPeerURLExist
return nil, rpctypes.ErrGRPCPeerURLExist
case err == membership.ErrIDNotFound:
return nil, rpctypes.ErrMemberNotFound
return nil, rpctypes.ErrGRPCMemberNotFound
case err != nil:
return nil, grpc.Errorf(codes.Internal, err.Error())
}
Expand Down
2 changes: 1 addition & 1 deletion etcdserver/api/v3rpc/quota.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (qa *quotaAlarmer) check(ctx context.Context, r interface{}) error {
Alarm: pb.AlarmType_NOSPACE,
}
qa.a.Alarm(ctx, req)
return rpctypes.ErrNoSpace
return rpctypes.ErrGRPCNoSpace
}

func NewQuotaKVServer(s *etcdserver.EtcdServer) pb.KVServer {
Expand Down
128 changes: 85 additions & 43 deletions etcdserver/api/v3rpc/rpctypes/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,62 +20,104 @@ import (
)

var (
ErrEmptyKey = grpc.Errorf(codes.InvalidArgument, "etcdserver: key is not provided")
ErrTooManyOps = grpc.Errorf(codes.InvalidArgument, "etcdserver: too many operations in txn request")
ErrDuplicateKey = grpc.Errorf(codes.InvalidArgument, "etcdserver: duplicate key given in txn request")
ErrCompacted = grpc.Errorf(codes.OutOfRange, "etcdserver: mvcc: required revision has been compacted")
ErrFutureRev = grpc.Errorf(codes.OutOfRange, "etcdserver: mvcc: required revision is a future revision")
ErrNoSpace = grpc.Errorf(codes.ResourceExhausted, "etcdserver: mvcc: database space exceeded")
// server-side error
ErrGRPCEmptyKey = grpc.Errorf(codes.InvalidArgument, "etcdserver: key is not provided")
ErrGRPCTooManyOps = grpc.Errorf(codes.InvalidArgument, "etcdserver: too many operations in txn request")
ErrGRPCDuplicateKey = grpc.Errorf(codes.InvalidArgument, "etcdserver: duplicate key given in txn request")
ErrGRPCCompacted = grpc.Errorf(codes.OutOfRange, "etcdserver: mvcc: required revision has been compacted")
ErrGRPCFutureRev = grpc.Errorf(codes.OutOfRange, "etcdserver: mvcc: required revision is a future revision")
ErrGRPCNoSpace = grpc.Errorf(codes.ResourceExhausted, "etcdserver: mvcc: database space exceeded")

ErrLeaseNotFound = grpc.Errorf(codes.NotFound, "etcdserver: requested lease not found")
ErrLeaseExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: lease already exists")
ErrGRPCLeaseNotFound = grpc.Errorf(codes.NotFound, "etcdserver: requested lease not found")
ErrGRPCLeaseExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: lease already exists")

ErrMemberExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: member ID already exist")
ErrPeerURLExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: Peer URLs already exists")
ErrMemberBadURLs = grpc.Errorf(codes.InvalidArgument, "etcdserver: given member URLs are invalid")
ErrMemberNotFound = grpc.Errorf(codes.NotFound, "etcdserver: member not found")
ErrGRPCMemberExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: member ID already exist")
ErrGRPCPeerURLExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: Peer URLs already exists")
ErrGRPCMemberBadURLs = grpc.Errorf(codes.InvalidArgument, "etcdserver: given member URLs are invalid")
ErrGRPCMemberNotFound = grpc.Errorf(codes.NotFound, "etcdserver: member not found")

ErrRequestTooLarge = grpc.Errorf(codes.InvalidArgument, "etcdserver: request is too large")
ErrGRPCRequestTooLarge = grpc.Errorf(codes.InvalidArgument, "etcdserver: request is too large")

ErrUserAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name already exists")
ErrUserNotFound = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name not found")
ErrRoleAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name already exists")
ErrRoleNotFound = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name not found")
ErrAuthFailed = grpc.Errorf(codes.InvalidArgument, "etcdserver: authentication failed, invalid user ID or password")
ErrGRPCUserAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name already exists")
ErrGRPCUserNotFound = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name not found")
ErrGRPCRoleAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name already exists")
ErrGRPCRoleNotFound = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name not found")
ErrGRPCAuthFailed = grpc.Errorf(codes.InvalidArgument, "etcdserver: authentication failed, invalid user ID or password")

errStringToError = map[string]error{
grpc.ErrorDesc(ErrEmptyKey): ErrEmptyKey,
grpc.ErrorDesc(ErrTooManyOps): ErrTooManyOps,
grpc.ErrorDesc(ErrDuplicateKey): ErrDuplicateKey,
grpc.ErrorDesc(ErrCompacted): ErrCompacted,
grpc.ErrorDesc(ErrFutureRev): ErrFutureRev,
grpc.ErrorDesc(ErrNoSpace): ErrNoSpace,

grpc.ErrorDesc(ErrLeaseNotFound): ErrLeaseNotFound,
grpc.ErrorDesc(ErrLeaseExist): ErrLeaseExist,

grpc.ErrorDesc(ErrMemberExist): ErrMemberExist,
grpc.ErrorDesc(ErrPeerURLExist): ErrPeerURLExist,
grpc.ErrorDesc(ErrMemberBadURLs): ErrMemberBadURLs,
grpc.ErrorDesc(ErrMemberNotFound): ErrMemberNotFound,

grpc.ErrorDesc(ErrRequestTooLarge): ErrRequestTooLarge,

grpc.ErrorDesc(ErrUserAlreadyExist): ErrUserAlreadyExist,
grpc.ErrorDesc(ErrUserNotFound): ErrUserNotFound,
grpc.ErrorDesc(ErrRoleAlreadyExist): ErrRoleAlreadyExist,
grpc.ErrorDesc(ErrRoleNotFound): ErrRoleNotFound,
grpc.ErrorDesc(ErrAuthFailed): ErrAuthFailed,
grpc.ErrorDesc(ErrGRPCEmptyKey): ErrGRPCEmptyKey,
grpc.ErrorDesc(ErrGRPCTooManyOps): ErrGRPCTooManyOps,
grpc.ErrorDesc(ErrGRPCDuplicateKey): ErrGRPCDuplicateKey,
grpc.ErrorDesc(ErrGRPCCompacted): ErrGRPCCompacted,
grpc.ErrorDesc(ErrGRPCFutureRev): ErrGRPCFutureRev,
grpc.ErrorDesc(ErrGRPCNoSpace): ErrGRPCNoSpace,

grpc.ErrorDesc(ErrGRPCLeaseNotFound): ErrGRPCLeaseNotFound,
grpc.ErrorDesc(ErrGRPCLeaseExist): ErrGRPCLeaseExist,

grpc.ErrorDesc(ErrGRPCMemberExist): ErrGRPCMemberExist,
grpc.ErrorDesc(ErrGRPCPeerURLExist): ErrGRPCPeerURLExist,
grpc.ErrorDesc(ErrGRPCMemberBadURLs): ErrGRPCMemberBadURLs,
grpc.ErrorDesc(ErrGRPCMemberNotFound): ErrGRPCMemberNotFound,

grpc.ErrorDesc(ErrGRPCRequestTooLarge): ErrGRPCRequestTooLarge,

grpc.ErrorDesc(ErrGRPCUserAlreadyExist): ErrGRPCUserAlreadyExist,
grpc.ErrorDesc(ErrGRPCUserNotFound): ErrGRPCUserNotFound,
grpc.ErrorDesc(ErrGRPCRoleAlreadyExist): ErrGRPCRoleAlreadyExist,
grpc.ErrorDesc(ErrGRPCRoleNotFound): ErrGRPCRoleNotFound,
grpc.ErrorDesc(ErrGRPCAuthFailed): ErrGRPCAuthFailed,
}

// client-side error
ErrEmptyKey = Error(ErrGRPCEmptyKey)
ErrTooManyOps = Error(ErrGRPCTooManyOps)
ErrDuplicateKey = Error(ErrGRPCDuplicateKey)
ErrCompacted = Error(ErrGRPCCompacted)
ErrFutureRev = Error(ErrGRPCFutureRev)
ErrNoSpace = Error(ErrGRPCNoSpace)

ErrLeaseNotFound = Error(ErrGRPCLeaseNotFound)
ErrLeaseExist = Error(ErrGRPCLeaseExist)

ErrMemberExist = Error(ErrGRPCMemberExist)
ErrPeerURLExist = Error(ErrGRPCPeerURLExist)
ErrMemberBadURLs = Error(ErrGRPCMemberBadURLs)
ErrMemberNotFound = Error(ErrGRPCMemberNotFound)

ErrRequestTooLarge = Error(ErrGRPCRequestTooLarge)

ErrUserAlreadyExist = Error(ErrGRPCUserAlreadyExist)
ErrUserNotFound = Error(ErrGRPCUserNotFound)
ErrRoleAlreadyExist = Error(ErrGRPCRoleAlreadyExist)
ErrRoleNotFound = Error(ErrGRPCRoleNotFound)
ErrAuthFailed = Error(ErrGRPCAuthFailed)
)

// EtcdError defines gRPC server errors.
// (https://github.com/grpc/grpc-go/blob/master/rpc_util.go#L319-L323)
type EtcdError struct {
code codes.Code
desc string
}

// Code returns grpc/codes.Code.
// TODO: define clientv3/codes.Code.
func (e EtcdError) Code() codes.Code {
return e.code
}

func (e EtcdError) Error() string {
return e.desc
}

func Error(err error) error {
if err == nil {
return nil
}
v, ok := errStringToError[err.Error()]
if !ok {
verr, ok := errStringToError[grpc.ErrorDesc(err)]
if !ok { // not gRPC error
return err
}
return v
return EtcdError{code: grpc.Code(verr), desc: grpc.ErrorDesc(verr)}
}
Loading

0 comments on commit 3396805

Please sign in to comment.