mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #4269 from heyitsanthony/v3-reject-put-bogus-lease
etcdserver: return error when putting a key with a bad lease id
This commit is contained in:
commit
5099bf6f7a
@ -196,11 +196,11 @@ func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) interface{} {
|
|||||||
case r.Range != nil:
|
case r.Range != nil:
|
||||||
ar.resp, ar.err = applyRange(noTxn, kv, r.Range)
|
ar.resp, ar.err = applyRange(noTxn, kv, r.Range)
|
||||||
case r.Put != nil:
|
case r.Put != nil:
|
||||||
ar.resp, ar.err = applyPut(noTxn, kv, r.Put)
|
ar.resp, ar.err = applyPut(noTxn, kv, le, r.Put)
|
||||||
case r.DeleteRange != nil:
|
case r.DeleteRange != nil:
|
||||||
ar.resp, ar.err = applyDeleteRange(noTxn, kv, r.DeleteRange)
|
ar.resp, ar.err = applyDeleteRange(noTxn, kv, r.DeleteRange)
|
||||||
case r.Txn != nil:
|
case r.Txn != nil:
|
||||||
ar.resp, ar.err = applyTxn(kv, r.Txn)
|
ar.resp, ar.err = applyTxn(kv, le, r.Txn)
|
||||||
case r.Compaction != nil:
|
case r.Compaction != nil:
|
||||||
ar.resp, ar.err = applyCompaction(kv, r.Compaction)
|
ar.resp, ar.err = applyCompaction(kv, r.Compaction)
|
||||||
case r.LeaseCreate != nil:
|
case r.LeaseCreate != nil:
|
||||||
@ -214,7 +214,7 @@ func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) interface{} {
|
|||||||
return ar
|
return ar
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyPut(txnID int64, kv dstorage.KV, p *pb.PutRequest) (*pb.PutResponse, error) {
|
func applyPut(txnID int64, kv dstorage.KV, le lease.Lessor, p *pb.PutRequest) (*pb.PutResponse, error) {
|
||||||
resp := &pb.PutResponse{}
|
resp := &pb.PutResponse{}
|
||||||
resp.Header = &pb.ResponseHeader{}
|
resp.Header = &pb.ResponseHeader{}
|
||||||
var (
|
var (
|
||||||
@ -227,7 +227,13 @@ func applyPut(txnID int64, kv dstorage.KV, p *pb.PutRequest) (*pb.PutResponse, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rev = kv.Put(p.Key, p.Value, lease.LeaseID(p.Lease))
|
leaseID := lease.LeaseID(p.Lease)
|
||||||
|
if leaseID != lease.NoLease {
|
||||||
|
if l := le.Lookup(leaseID); l == nil {
|
||||||
|
return nil, lease.ErrLeaseNotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rev = kv.Put(p.Key, p.Value, leaseID)
|
||||||
}
|
}
|
||||||
resp.Header.Revision = rev
|
resp.Header.Revision = rev
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -360,7 +366,20 @@ func applyDeleteRange(txnID int64, kv dstorage.KV, dr *pb.DeleteRangeRequest) (*
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyTxn(kv dstorage.KV, rt *pb.TxnRequest) (*pb.TxnResponse, error) {
|
func checkRequestLeases(le lease.Lessor, reqs []*pb.RequestUnion) error {
|
||||||
|
for _, requ := range reqs {
|
||||||
|
preq := requ.RequestPut
|
||||||
|
if preq == nil || lease.LeaseID(preq.Lease) == lease.NoLease {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if l := le.Lookup(lease.LeaseID(preq.Lease)); l == nil {
|
||||||
|
return lease.ErrLeaseNotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyTxn(kv dstorage.KV, le lease.Lessor, rt *pb.TxnRequest) (*pb.TxnResponse, error) {
|
||||||
var revision int64
|
var revision int64
|
||||||
|
|
||||||
txnID := kv.TxnBegin()
|
txnID := kv.TxnBegin()
|
||||||
@ -387,6 +406,10 @@ func applyTxn(kv dstorage.KV, rt *pb.TxnRequest) (*pb.TxnResponse, error) {
|
|||||||
reqs = rt.Failure
|
reqs = rt.Failure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := checkRequestLeases(le, reqs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
resps := make([]*pb.ResponseUnion, len(reqs))
|
resps := make([]*pb.ResponseUnion, len(reqs))
|
||||||
for i := range reqs {
|
for i := range reqs {
|
||||||
resps[i] = applyUnion(txnID, kv, reqs[i])
|
resps[i] = applyUnion(txnID, kv, reqs[i])
|
||||||
@ -425,7 +448,7 @@ func applyUnion(txnID int64, kv dstorage.KV, union *pb.RequestUnion) *pb.Respons
|
|||||||
}
|
}
|
||||||
return &pb.ResponseUnion{ResponseRange: resp}
|
return &pb.ResponseUnion{ResponseRange: resp}
|
||||||
case union.RequestPut != nil:
|
case union.RequestPut != nil:
|
||||||
resp, err := applyPut(txnID, kv, union.RequestPut)
|
resp, err := applyPut(txnID, kv, nil, union.RequestPut)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("unexpected error during txn")
|
panic("unexpected error during txn")
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,68 @@ func TestV3PutOverwrite(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestV3PutMissingLease ensures that a Put on a key with a bogus lease fails.
|
||||||
|
func TestV3PutMissingLease(t *testing.T) {
|
||||||
|
clus := newClusterGRPC(t, &clusterConfig{size: 3})
|
||||||
|
defer clus.Terminate(t)
|
||||||
|
|
||||||
|
kvc := pb.NewKVClient(clus.RandConn())
|
||||||
|
key := []byte("foo")
|
||||||
|
preq := &pb.PutRequest{Key: key, Lease: 123456}
|
||||||
|
tests := []func(){
|
||||||
|
// put case
|
||||||
|
func() {
|
||||||
|
if presp, err := kvc.Put(context.TODO(), preq); err == nil {
|
||||||
|
t.Errorf("succeeded put key. req: %v. resp: %v", preq, presp)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// txn success case
|
||||||
|
func() {
|
||||||
|
txn := &pb.TxnRequest{}
|
||||||
|
txn.Success = append(txn.Success, &pb.RequestUnion{RequestPut: preq})
|
||||||
|
if tresp, err := kvc.Txn(context.TODO(), txn); err == nil {
|
||||||
|
t.Errorf("succeeded txn success. req: %v. resp: %v", txn, tresp)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// txn failure case
|
||||||
|
func() {
|
||||||
|
txn := &pb.TxnRequest{}
|
||||||
|
txn.Failure = append(txn.Failure, &pb.RequestUnion{RequestPut: preq})
|
||||||
|
cmp := &pb.Compare{
|
||||||
|
Result: pb.Compare_GREATER,
|
||||||
|
Target: pb.Compare_CREATE,
|
||||||
|
Key: []byte("bar"),
|
||||||
|
}
|
||||||
|
txn.Compare = append(txn.Compare, cmp)
|
||||||
|
if tresp, err := kvc.Txn(context.TODO(), txn); err == nil {
|
||||||
|
t.Errorf("succeeded txn failure. req: %v. resp: %v", txn, tresp)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// ignore bad lease in failure on success txn
|
||||||
|
func() {
|
||||||
|
txn := &pb.TxnRequest{}
|
||||||
|
rreq := &pb.RangeRequest{Key: []byte("bar")}
|
||||||
|
txn.Success = append(txn.Success, &pb.RequestUnion{RequestRange: rreq})
|
||||||
|
txn.Failure = append(txn.Failure, &pb.RequestUnion{RequestPut: preq})
|
||||||
|
if tresp, err := kvc.Txn(context.TODO(), txn); err != nil {
|
||||||
|
t.Errorf("failed good txn. req: %v. resp: %v", txn, tresp)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, f := range tests {
|
||||||
|
f()
|
||||||
|
// key shouldn't have been stored
|
||||||
|
rreq := &pb.RangeRequest{Key: key}
|
||||||
|
rresp, err := kvc.Range(context.TODO(), rreq)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("#%d. could not rangereq (%v)", i, err)
|
||||||
|
} else if len(rresp.Kvs) != 0 {
|
||||||
|
t.Errorf("#%d. expected no keys, got %v", i, rresp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestV3DeleteRange tests various edge cases in the DeleteRange API.
|
// TestV3DeleteRange tests various edge cases in the DeleteRange API.
|
||||||
func TestV3DeleteRange(t *testing.T) {
|
func TestV3DeleteRange(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
@ -83,6 +83,9 @@ type Lessor interface {
|
|||||||
// an error will be returned.
|
// an error will be returned.
|
||||||
Renew(id LeaseID) (int64, error)
|
Renew(id LeaseID) (int64, error)
|
||||||
|
|
||||||
|
// Lookup gives the lease at a given lease id, if any
|
||||||
|
Lookup(id LeaseID) *Lease
|
||||||
|
|
||||||
// ExpiredLeasesC returns a chan that is used to receive expired leases.
|
// ExpiredLeasesC returns a chan that is used to receive expired leases.
|
||||||
ExpiredLeasesC() <-chan []*Lease
|
ExpiredLeasesC() <-chan []*Lease
|
||||||
|
|
||||||
@ -230,6 +233,15 @@ func (le *lessor) Renew(id LeaseID) (int64, error) {
|
|||||||
return l.TTL, nil
|
return l.TTL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (le *lessor) Lookup(id LeaseID) *Lease {
|
||||||
|
le.mu.Lock()
|
||||||
|
defer le.mu.Unlock()
|
||||||
|
if l, ok := le.leaseMap[id]; ok {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (le *lessor) Promote() {
|
func (le *lessor) Promote() {
|
||||||
le.mu.Lock()
|
le.mu.Lock()
|
||||||
defer le.mu.Unlock()
|
defer le.mu.Unlock()
|
||||||
@ -456,6 +468,8 @@ func (fl *FakeLessor) Demote() {}
|
|||||||
|
|
||||||
func (fl *FakeLessor) Renew(id LeaseID) (int64, error) { return 10, nil }
|
func (fl *FakeLessor) Renew(id LeaseID) (int64, error) { return 10, nil }
|
||||||
|
|
||||||
|
func (le *FakeLessor) Lookup(id LeaseID) *Lease { return nil }
|
||||||
|
|
||||||
func (fl *FakeLessor) ExpiredLeasesC() <-chan []*Lease { return nil }
|
func (fl *FakeLessor) ExpiredLeasesC() <-chan []*Lease { return nil }
|
||||||
|
|
||||||
func (fl *FakeLessor) Stop() {}
|
func (fl *FakeLessor) Stop() {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user