mirror of https://github.com/status-im/consul.git
Issue #2905: Add check-not-exists to TXN endpoint
This patch adds support for asserting that a given key does not exist in the KV store. Fixes #2905
This commit is contained in:
parent
eddb1af603
commit
6dd2804d09
|
@ -60,6 +60,7 @@ const (
|
|||
KVGetTree KVOp = "get-tree"
|
||||
KVCheckSession KVOp = "check-session"
|
||||
KVCheckIndex KVOp = "check-index"
|
||||
KVCheckNotExists KVOp = "check-not-exists"
|
||||
)
|
||||
|
||||
// KVTxnOp defines a single operation inside a transaction.
|
||||
|
|
|
@ -79,6 +79,12 @@ func (s *StateStore) txnKVS(tx *memdb.Txn, idx uint64, op *structs.TxnKVOp) (str
|
|||
case api.KVCheckIndex:
|
||||
entry, err = s.kvsCheckIndexTxn(tx, op.DirEnt.Key, op.DirEnt.ModifyIndex)
|
||||
|
||||
case api.KVCheckNotExists:
|
||||
_, entry, err = s.kvsGetTxn(tx, nil, op.DirEnt.Key)
|
||||
if entry != nil && err == nil {
|
||||
err = fmt.Errorf("key %q exists", op.DirEnt.Key)
|
||||
}
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("unknown KV verb %q", op.Verb)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,57 @@ import (
|
|||
"github.com/hashicorp/net-rpc-msgpackrpc"
|
||||
)
|
||||
|
||||
func TestTxn_CheckNotExists(t *testing.T) {
|
||||
dir1, s1 := testServer(t)
|
||||
defer os.RemoveAll(dir1)
|
||||
defer s1.Shutdown()
|
||||
codec := rpcClient(t, s1)
|
||||
defer codec.Close()
|
||||
|
||||
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
||||
|
||||
apply := func(arg *structs.TxnRequest) (*structs.TxnResponse, error) {
|
||||
out := new(structs.TxnResponse)
|
||||
err := msgpackrpc.CallWithCodec(codec, "Txn.Apply", arg, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
checkKeyNotExists := &structs.TxnRequest{
|
||||
Datacenter: "dc1",
|
||||
Ops: structs.TxnOps{
|
||||
{
|
||||
KV: &structs.TxnKVOp{
|
||||
Verb: api.KVCheckNotExists,
|
||||
DirEnt: structs.DirEntry{Key: "test"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
createKey := &structs.TxnRequest{
|
||||
Datacenter: "dc1",
|
||||
Ops: structs.TxnOps{
|
||||
{
|
||||
KV: &structs.TxnKVOp{
|
||||
Verb: api.KVSet,
|
||||
DirEnt: structs.DirEntry{Key: "test"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := apply(checkKeyNotExists); err != nil {
|
||||
t.Fatalf("testing for non-existent key failed: %s", err)
|
||||
}
|
||||
if _, err := apply(createKey); err != nil {
|
||||
t.Fatalf("creating new key failed: %s", err)
|
||||
}
|
||||
out, err := apply(checkKeyNotExists)
|
||||
if err != nil || out == nil || len(out.Errors) != 1 || out.Errors[0].Error() != `op 0: key "test" exists` {
|
||||
t.Fatalf("testing for existent key failed: %#v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxn_Apply(t *testing.T) {
|
||||
dir1, s1 := testServer(t)
|
||||
defer os.RemoveAll(dir1)
|
||||
|
@ -234,6 +285,14 @@ func TestTxn_Apply_ACLDeny(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
&structs.TxnOp{
|
||||
KV: &structs.TxnKVOp{
|
||||
Verb: api.KVCheckNotExists,
|
||||
DirEnt: structs.DirEntry{
|
||||
Key: "nope",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
WriteRequest: structs.WriteRequest{
|
||||
Token: id,
|
||||
|
|
|
@ -150,7 +150,7 @@ The following table summarizes the available verbs and the fields that apply to
|
|||
that operation ("X" means a field is required and "O" means it is optional):
|
||||
|
||||
| Verb | Operation | Key | Value | Flags | Index | Session |
|
||||
| --------------- | -------------------------------------------- | :--: | :---: | :---: | :---: | :-----: |
|
||||
| ------------------ | -------------------------------------------- | :--: | :---: | :---: | :---: | :-----: |
|
||||
| `set` | Sets the `Key` to the given `Value` | `x` | `x` | `o` | | |
|
||||
| `cas` | Sets, but with CAS semantics | `x` | `x` | `o` | `x` | |
|
||||
| `lock` | Lock with the given `Session` | `x` | `x` | `o` | | `x` |
|
||||
|
@ -159,6 +159,7 @@ that operation ("X" means a field is required and "O" means it is optional):
|
|||
| `get-tree` | Gets all keys with the prefix | `x` | | | | |
|
||||
| `check-index` | Fail if modify index != index | `x` | | | `x` | |
|
||||
| `check-session` | Fail if not locked by session | `x` | | | | `x` |
|
||||
| `check-not-exists` | Fail if key exists | `x` | | | | |
|
||||
| `delete` | Delete the key | `x` | | | | |
|
||||
| `delete-tree` | Delete all keys with a prefix | `x` | | | | |
|
||||
| `delete-cas` | Delete, but with CAS semantics | `x` | | | `x` | |
|
||||
|
|
Loading…
Reference in New Issue