Browse Source

Merge pull request #5222 from gyuho/error_interface

rpctypes: error interface
Gyu-Ho Lee 9 years ago
parent
commit
33968059e9

+ 9 - 8
clientv3/auth.go

@@ -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"
@@ -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) {
@@ -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) {

+ 1 - 1
clientv3/integration/kv_test.go

@@ -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()

+ 3 - 5
clientv3/integration/lease_test.go

@@ -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})
@@ -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)
 	}
 }
 

+ 1 - 1
clientv3/integration/watch_test.go

@@ -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

+ 9 - 9
etcdserver/api/v3rpc/key.go

@@ -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
 		}
 	}
 
@@ -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{})
@@ -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{}{}
 	}
@@ -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
 			}
 		}
 	}

+ 2 - 2
etcdserver/api/v3rpc/lease.go

@@ -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
@@ -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

+ 6 - 6
etcdserver/api/v3rpc/member.go

@@ -45,7 +45,7 @@ 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()
@@ -53,9 +53,9 @@ func (cs *ClusterServer) MemberAdd(ctx context.Context, r *pb.MemberAddRequest)
 	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())
 	}
@@ -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())
 	}
@@ -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())
 	}

+ 1 - 1
etcdserver/api/v3rpc/quota.go

@@ -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 {

+ 85 - 43
etcdserver/api/v3rpc/rpctypes/error.go

@@ -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)}
 }

+ 42 - 0
etcdserver/api/v3rpc/rpctypes/error_test.go

@@ -0,0 +1,42 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rpctypes
+
+import (
+	"testing"
+
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+)
+
+func TestConvert(t *testing.T) {
+	e1 := grpc.Errorf(codes.InvalidArgument, "etcdserver: key is not provided")
+	e2 := ErrGRPCEmptyKey
+	e3 := ErrEmptyKey
+
+	if e1 != e2 {
+		t.Fatalf("expected 'true', got %T != %T", e1, e2)
+	}
+	if grpc.Code(e1) != e3.(EtcdError).Code() {
+		t.Fatalf("expected them to be equal, got %v / %v", grpc.Code(e1), e3.(EtcdError).Code())
+	}
+
+	if e1 == e3 {
+		t.Fatalf("expected 'false', got %T == %T", e1, e3)
+	}
+	if grpc.Code(e2) != e3.(EtcdError).Code() {
+		t.Fatalf("expected them to be equal, got %v / %v", grpc.Code(e2), e3.(EtcdError).Code())
+	}
+}

+ 10 - 10
etcdserver/api/v3rpc/util.go

@@ -27,26 +27,26 @@ import (
 func togRPCError(err error) error {
 	switch err {
 	case mvcc.ErrCompacted:
-		return rpctypes.ErrCompacted
+		return rpctypes.ErrGRPCCompacted
 	case mvcc.ErrFutureRev:
-		return rpctypes.ErrFutureRev
+		return rpctypes.ErrGRPCFutureRev
 	case lease.ErrLeaseNotFound:
-		return rpctypes.ErrLeaseNotFound
+		return rpctypes.ErrGRPCLeaseNotFound
 	// TODO: handle error from raft and timeout
 	case etcdserver.ErrRequestTooLarge:
-		return rpctypes.ErrRequestTooLarge
+		return rpctypes.ErrGRPCRequestTooLarge
 	case etcdserver.ErrNoSpace:
-		return rpctypes.ErrNoSpace
+		return rpctypes.ErrGRPCNoSpace
 	case auth.ErrUserAlreadyExist:
-		return rpctypes.ErrUserAlreadyExist
+		return rpctypes.ErrGRPCUserAlreadyExist
 	case auth.ErrUserNotFound:
-		return rpctypes.ErrUserNotFound
+		return rpctypes.ErrGRPCUserNotFound
 	case auth.ErrRoleAlreadyExist:
-		return rpctypes.ErrRoleAlreadyExist
+		return rpctypes.ErrGRPCRoleAlreadyExist
 	case auth.ErrRoleNotFound:
-		return rpctypes.ErrRoleNotFound
+		return rpctypes.ErrGRPCRoleNotFound
 	case auth.ErrAuthFailed:
-		return rpctypes.ErrAuthFailed
+		return rpctypes.ErrGRPCAuthFailed
 	default:
 		return grpc.Errorf(codes.Internal, err.Error())
 	}

+ 22 - 10
integration/cluster.go

@@ -196,8 +196,13 @@ func (c *cluster) HTTPMembers() []client.Member {
 }
 
 func (c *cluster) mustNewMember(t *testing.T) *member {
-	name := c.name(rand.Int())
-	m := mustNewMember(t, name, c.cfg.PeerTLS, c.cfg.ClientTLS, c.cfg.QuotaBackendBytes)
+	m := mustNewMember(t,
+		memberConfig{
+			name:              c.name(rand.Int()),
+			peerTLS:           c.cfg.PeerTLS,
+			clientTLS:         c.cfg.ClientTLS,
+			quotaBackendBytes: c.cfg.QuotaBackendBytes,
+		})
 	m.DiscoveryURL = c.cfg.DiscoveryURL
 	if c.cfg.UseGRPC {
 		if err := m.listenGRPC(); err != nil {
@@ -416,17 +421,24 @@ type member struct {
 	grpcAddr   string
 }
 
+type memberConfig struct {
+	name              string
+	peerTLS           *transport.TLSInfo
+	clientTLS         *transport.TLSInfo
+	quotaBackendBytes int64
+}
+
 // mustNewMember return an inited member with the given name. If peerTLS is
 // set, it will use https scheme to communicate between peers.
-func mustNewMember(t *testing.T, name string, peerTLS *transport.TLSInfo, clientTLS *transport.TLSInfo, quotaBackendBytes int64) *member {
+func mustNewMember(t *testing.T, mcfg memberConfig) *member {
 	var err error
 	m := &member{}
 
 	peerScheme, clientScheme := "http", "http"
-	if peerTLS != nil {
+	if mcfg.peerTLS != nil {
 		peerScheme = "https"
 	}
-	if clientTLS != nil {
+	if mcfg.clientTLS != nil {
 		clientScheme = "https"
 	}
 
@@ -436,7 +448,7 @@ func mustNewMember(t *testing.T, name string, peerTLS *transport.TLSInfo, client
 	if err != nil {
 		t.Fatal(err)
 	}
-	m.PeerTLSInfo = peerTLS
+	m.PeerTLSInfo = mcfg.peerTLS
 
 	cln := newLocalListener(t)
 	m.ClientListeners = []net.Listener{cln}
@@ -444,15 +456,15 @@ func mustNewMember(t *testing.T, name string, peerTLS *transport.TLSInfo, client
 	if err != nil {
 		t.Fatal(err)
 	}
-	m.ClientTLSInfo = clientTLS
+	m.ClientTLSInfo = mcfg.clientTLS
 
-	m.Name = name
+	m.Name = mcfg.name
 
 	m.DataDir, err = ioutil.TempDir(os.TempDir(), "etcd")
 	if err != nil {
 		t.Fatal(err)
 	}
-	clusterStr := fmt.Sprintf("%s=%s://%s", name, peerScheme, pln.Addr().String())
+	clusterStr := fmt.Sprintf("%s=%s://%s", mcfg.name, peerScheme, pln.Addr().String())
 	m.InitialPeerURLsMap, err = types.NewURLsMap(clusterStr)
 	if err != nil {
 		t.Fatal(err)
@@ -465,7 +477,7 @@ func mustNewMember(t *testing.T, name string, peerTLS *transport.TLSInfo, client
 	}
 	m.ElectionTicks = electionTicks
 	m.TickMs = uint(tickDuration / time.Millisecond)
-	m.QuotaBackendBytes = quotaBackendBytes
+	m.QuotaBackendBytes = mcfg.quotaBackendBytes
 	return m
 }
 

+ 1 - 1
integration/member_test.go

@@ -84,7 +84,7 @@ func TestLaunchDuplicateMemberShouldFail(t *testing.T) {
 
 func TestSnapshotAndRestartMember(t *testing.T) {
 	defer testutil.AfterTest(t)
-	m := mustNewMember(t, "snapAndRestartTest", nil, nil)
+	m := mustNewMember(t, memberConfig{name: "snapAndRestartTest"})
 	m.SnapCount = 100
 	m.Launch()
 	defer m.Terminate(t)

+ 1 - 1
integration/migration_test.go

@@ -23,7 +23,7 @@ import (
 
 func TestUpgradeMember(t *testing.T) {
 	defer testutil.AfterTest(t)
-	m := mustNewMember(t, "integration046", nil, nil)
+	m := mustNewMember(t, memberConfig{name: "integration046"})
 	cmd := exec.Command("cp", "-r", "testdata/integration046_data/conf", "testdata/integration046_data/log", "testdata/integration046_data/snapshot", m.DataDir)
 	err := cmd.Run()
 	if err != nil {

+ 17 - 17
integration/v3_grpc_test.go

@@ -193,8 +193,8 @@ func TestV3TxnTooManyOps(t *testing.T) {
 		}
 
 		_, err := kvc.Txn(context.Background(), txn)
-		if err != rpctypes.ErrTooManyOps {
-			t.Errorf("#%d: err = %v, want %v", i, err, rpctypes.ErrTooManyOps)
+		if err != rpctypes.ErrGRPCTooManyOps {
+			t.Errorf("#%d: err = %v, want %v", i, err, rpctypes.ErrGRPCTooManyOps)
 		}
 	}
 }
@@ -233,17 +233,17 @@ func TestV3TxnDuplicateKeys(t *testing.T) {
 		{
 			txnSuccess: []*pb.RequestUnion{putreq, putreq},
 
-			werr: rpctypes.ErrDuplicateKey,
+			werr: rpctypes.ErrGRPCDuplicateKey,
 		},
 		{
 			txnSuccess: []*pb.RequestUnion{putreq, delKeyReq},
 
-			werr: rpctypes.ErrDuplicateKey,
+			werr: rpctypes.ErrGRPCDuplicateKey,
 		},
 		{
 			txnSuccess: []*pb.RequestUnion{putreq, delInRangeReq},
 
-			werr: rpctypes.ErrDuplicateKey,
+			werr: rpctypes.ErrGRPCDuplicateKey,
 		},
 		{
 			txnSuccess: []*pb.RequestUnion{delKeyReq, delInRangeReq, delKeyReq, delInRangeReq},
@@ -502,15 +502,15 @@ func TestV3TxnInvaildRange(t *testing.T) {
 		Request: &pb.RequestUnion_RequestRange{
 			RequestRange: rreq}})
 
-	if _, err := kvc.Txn(context.TODO(), txn); err != rpctypes.ErrFutureRev {
-		t.Errorf("err = %v, want %v", err, rpctypes.ErrFutureRev)
+	if _, err := kvc.Txn(context.TODO(), txn); err != rpctypes.ErrGRPCFutureRev {
+		t.Errorf("err = %v, want %v", err, rpctypes.ErrGRPCFutureRev)
 	}
 
 	// compacted rev
 	tv, _ := txn.Success[1].Request.(*pb.RequestUnion_RequestRange)
 	tv.RequestRange.Revision = 1
-	if _, err := kvc.Txn(context.TODO(), txn); err != rpctypes.ErrCompacted {
-		t.Errorf("err = %v, want %v", err, rpctypes.ErrCompacted)
+	if _, err := kvc.Txn(context.TODO(), txn); err != rpctypes.ErrGRPCCompacted {
+		t.Errorf("err = %v, want %v", err, rpctypes.ErrGRPCCompacted)
 	}
 }
 
@@ -527,8 +527,8 @@ func TestV3TooLargeRequest(t *testing.T) {
 	preq := &pb.PutRequest{Key: []byte("foo"), Value: largeV}
 
 	_, err := kvc.Put(context.Background(), preq)
-	if err != rpctypes.ErrRequestTooLarge {
-		t.Errorf("err = %v, want %v", err, rpctypes.ErrRequestTooLarge)
+	if err != rpctypes.ErrGRPCRequestTooLarge {
+		t.Errorf("err = %v, want %v", err, rpctypes.ErrGRPCRequestTooLarge)
 	}
 }
 
@@ -581,8 +581,8 @@ func TestV3StorageQuotaAPI(t *testing.T) {
 	// test big put
 	bigbuf := make([]byte, 64*1024)
 	_, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: key, Value: bigbuf})
-	if err == nil || err != rpctypes.ErrNoSpace {
-		t.Fatalf("big put got %v, expected %v", err, rpctypes.ErrNoSpace)
+	if err == nil || err != rpctypes.ErrGRPCNoSpace {
+		t.Fatalf("big put got %v, expected %v", err, rpctypes.ErrGRPCNoSpace)
 	}
 
 	// test big txn
@@ -597,8 +597,8 @@ func TestV3StorageQuotaAPI(t *testing.T) {
 	txnreq := &pb.TxnRequest{}
 	txnreq.Success = append(txnreq.Success, puttxn)
 	_, txnerr := kvc.Txn(context.TODO(), txnreq)
-	if txnerr == nil || err != rpctypes.ErrNoSpace {
-		t.Fatalf("big txn got %v, expected %v", err, rpctypes.ErrNoSpace)
+	if txnerr == nil || err != rpctypes.ErrGRPCNoSpace {
+		t.Fatalf("big txn got %v, expected %v", err, rpctypes.ErrGRPCNoSpace)
 	}
 }
 
@@ -696,8 +696,8 @@ func TestV3AlarmDeactivate(t *testing.T) {
 	key := []byte("abc")
 	smallbuf := make([]byte, 512)
 	_, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: key, Value: smallbuf})
-	if err == nil && err != rpctypes.ErrNoSpace {
-		t.Fatalf("put got %v, expected %v", err, rpctypes.ErrNoSpace)
+	if err == nil && err != rpctypes.ErrGRPCNoSpace {
+		t.Fatalf("put got %v, expected %v", err, rpctypes.ErrGRPCNoSpace)
 	}
 
 	alarmReq.Action = pb.AlarmRequest_DEACTIVATE

+ 4 - 4
integration/v3_lease_test.go

@@ -106,7 +106,7 @@ func TestV3LeaseGrantByID(t *testing.T) {
 	lresp, err = toGRPC(clus.RandClient()).Lease.LeaseGrant(
 		context.TODO(),
 		&pb.LeaseGrantRequest{ID: 1, TTL: 1})
-	if err != rpctypes.ErrLeaseExist {
+	if err != rpctypes.ErrGRPCLeaseExist {
 		t.Error(err)
 	}
 
@@ -242,8 +242,8 @@ func TestV3PutOnNonExistLease(t *testing.T) {
 	badLeaseID := int64(0x12345678)
 	putr := &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar"), Lease: badLeaseID}
 	_, err := toGRPC(clus.RandClient()).KV.Put(ctx, putr)
-	if err != rpctypes.ErrLeaseNotFound {
-		t.Errorf("err = %v, want %v", err, rpctypes.ErrCompacted)
+	if err != rpctypes.ErrGRPCLeaseNotFound {
+		t.Errorf("err = %v, want %v", err, rpctypes.ErrGRPCCompacted)
 	}
 }
 
@@ -424,7 +424,7 @@ func leaseExist(t *testing.T, clus *ClusterV3, leaseID int64) bool {
 		return false
 	}
 
-	if err == rpctypes.ErrLeaseExist {
+	if err == rpctypes.ErrGRPCLeaseExist {
 		return true
 	}
 	t.Fatalf("unexpecter error %v", err)

+ 1 - 1
test

@@ -28,7 +28,7 @@ ln -s ${PWD}/cmd/vendor $GOPATH/src
 
 # Hack: gofmt ./ will recursively check the .git directory. So use *.go for gofmt.
 PKGS=`ls pkg/*/*go  | cut -f1,2 -d/ | sort | uniq`
-TESTABLE_AND_FORMATTABLE="auth client clientv3 discovery error etcdctl/ctlv2 etcdctl/ctlv3 etcdmain etcdserver etcdserver/auth etcdserver/api/v2http etcdserver/api/v2http/httptypes $PKGS proxy/httpproxy proxy/tcpproxy raft snap mvcc mvcc/backend store version wal rafthttp"
+TESTABLE_AND_FORMATTABLE="auth client clientv3 discovery error etcdctl/ctlv2 etcdctl/ctlv3 etcdmain etcdserver etcdserver/auth etcdserver/api/v2http etcdserver/api/v2http/httptypes etcdserver/api/v3rpc etcdserver/api/v3rpc/rpctypes $PKGS proxy/httpproxy proxy/tcpproxy raft snap mvcc mvcc/backend store version wal rafthttp"
 FORMATTABLE="$TESTABLE_AND_FORMATTABLE *.go etcdctl/ integration clientv3/integration e2e alarm"
 
 # user has not provided PKG override