blob: aa338d5356d8d997e838a7d95c5223cd5c16ea08 [file] [log] [blame]
Abhay Kumara61c5222025-11-10 07:32:50 +00001// Copyright 2017 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package namespace
16
17import (
18 "context"
19
20 pb "go.etcd.io/etcd/api/v3/etcdserverpb"
21 "go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
22 clientv3 "go.etcd.io/etcd/client/v3"
23)
24
25type kvPrefix struct {
26 clientv3.KV
27 pfx string
28}
29
30// NewKV wraps a KV instance so that all requests
31// are prefixed with a given string.
32func NewKV(kv clientv3.KV, prefix string) clientv3.KV {
33 return &kvPrefix{kv, prefix}
34}
35
36func (kv *kvPrefix) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) {
37 if len(key) == 0 {
38 return nil, rpctypes.ErrEmptyKey
39 }
40 op := kv.prefixOp(clientv3.OpPut(key, val, opts...))
41 r, err := kv.KV.Do(ctx, op)
42 if err != nil {
43 return nil, err
44 }
45 put := r.Put()
46 kv.unprefixPutResponse(put)
47 return put, nil
48}
49
50func (kv *kvPrefix) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
51 if len(key) == 0 && !(clientv3.IsOptsWithFromKey(opts) || clientv3.IsOptsWithPrefix(opts)) {
52 return nil, rpctypes.ErrEmptyKey
53 }
54 getOp := clientv3.OpGet(key, opts...)
55 if !getOp.IsSortOptionValid() {
56 return nil, rpctypes.ErrInvalidSortOption
57 }
58 r, err := kv.KV.Do(ctx, kv.prefixOp(getOp))
59 if err != nil {
60 return nil, err
61 }
62 get := r.Get()
63 kv.unprefixGetResponse(get)
64 return get, nil
65}
66
67func (kv *kvPrefix) Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error) {
68 if len(key) == 0 && !(clientv3.IsOptsWithFromKey(opts) || clientv3.IsOptsWithPrefix(opts)) {
69 return nil, rpctypes.ErrEmptyKey
70 }
71 r, err := kv.KV.Do(ctx, kv.prefixOp(clientv3.OpDelete(key, opts...)))
72 if err != nil {
73 return nil, err
74 }
75 del := r.Del()
76 kv.unprefixDeleteResponse(del)
77 return del, nil
78}
79
80func (kv *kvPrefix) Do(ctx context.Context, op clientv3.Op) (clientv3.OpResponse, error) {
81 if len(op.KeyBytes()) == 0 && !op.IsTxn() {
82 return clientv3.OpResponse{}, rpctypes.ErrEmptyKey
83 }
84 r, err := kv.KV.Do(ctx, kv.prefixOp(op))
85 if err != nil {
86 return r, err
87 }
88 switch {
89 case r.Get() != nil:
90 kv.unprefixGetResponse(r.Get())
91 case r.Put() != nil:
92 kv.unprefixPutResponse(r.Put())
93 case r.Del() != nil:
94 kv.unprefixDeleteResponse(r.Del())
95 case r.Txn() != nil:
96 kv.unprefixTxnResponse(r.Txn())
97 }
98 return r, nil
99}
100
101type txnPrefix struct {
102 clientv3.Txn
103 kv *kvPrefix
104}
105
106func (kv *kvPrefix) Txn(ctx context.Context) clientv3.Txn {
107 return &txnPrefix{kv.KV.Txn(ctx), kv}
108}
109
110func (txn *txnPrefix) If(cs ...clientv3.Cmp) clientv3.Txn {
111 txn.Txn = txn.Txn.If(txn.kv.prefixCmps(cs)...)
112 return txn
113}
114
115func (txn *txnPrefix) Then(ops ...clientv3.Op) clientv3.Txn {
116 txn.Txn = txn.Txn.Then(txn.kv.prefixOps(ops)...)
117 return txn
118}
119
120func (txn *txnPrefix) Else(ops ...clientv3.Op) clientv3.Txn {
121 txn.Txn = txn.Txn.Else(txn.kv.prefixOps(ops)...)
122 return txn
123}
124
125func (txn *txnPrefix) Commit() (*clientv3.TxnResponse, error) {
126 resp, err := txn.Txn.Commit()
127 if err != nil {
128 return nil, err
129 }
130 txn.kv.unprefixTxnResponse(resp)
131 return resp, nil
132}
133
134func (kv *kvPrefix) prefixOp(op clientv3.Op) clientv3.Op {
135 if !op.IsTxn() {
136 begin, end := kv.prefixInterval(op.KeyBytes(), op.RangeBytes())
137 op.WithKeyBytes(begin)
138 op.WithRangeBytes(end)
139 return op
140 }
141 cmps, thenOps, elseOps := op.Txn()
142 return clientv3.OpTxn(kv.prefixCmps(cmps), kv.prefixOps(thenOps), kv.prefixOps(elseOps))
143}
144
145func (kv *kvPrefix) unprefixGetResponse(resp *clientv3.GetResponse) {
146 for i := range resp.Kvs {
147 resp.Kvs[i].Key = resp.Kvs[i].Key[len(kv.pfx):]
148 }
149}
150
151func (kv *kvPrefix) unprefixPutResponse(resp *clientv3.PutResponse) {
152 if resp.PrevKv != nil {
153 resp.PrevKv.Key = resp.PrevKv.Key[len(kv.pfx):]
154 }
155}
156
157func (kv *kvPrefix) unprefixDeleteResponse(resp *clientv3.DeleteResponse) {
158 for i := range resp.PrevKvs {
159 resp.PrevKvs[i].Key = resp.PrevKvs[i].Key[len(kv.pfx):]
160 }
161}
162
163func (kv *kvPrefix) unprefixTxnResponse(resp *clientv3.TxnResponse) {
164 for _, r := range resp.Responses {
165 switch tv := r.Response.(type) {
166 case *pb.ResponseOp_ResponseRange:
167 if tv.ResponseRange != nil {
168 kv.unprefixGetResponse((*clientv3.GetResponse)(tv.ResponseRange))
169 }
170 case *pb.ResponseOp_ResponsePut:
171 if tv.ResponsePut != nil {
172 kv.unprefixPutResponse((*clientv3.PutResponse)(tv.ResponsePut))
173 }
174 case *pb.ResponseOp_ResponseDeleteRange:
175 if tv.ResponseDeleteRange != nil {
176 kv.unprefixDeleteResponse((*clientv3.DeleteResponse)(tv.ResponseDeleteRange))
177 }
178 case *pb.ResponseOp_ResponseTxn:
179 if tv.ResponseTxn != nil {
180 kv.unprefixTxnResponse((*clientv3.TxnResponse)(tv.ResponseTxn))
181 }
182 default:
183 }
184 }
185}
186
187func (kv *kvPrefix) prefixInterval(key, end []byte) (pfxKey []byte, pfxEnd []byte) {
188 return prefixInterval(kv.pfx, key, end)
189}
190
191func (kv *kvPrefix) prefixCmps(cs []clientv3.Cmp) []clientv3.Cmp {
192 newCmps := make([]clientv3.Cmp, len(cs))
193 for i := range cs {
194 newCmps[i] = cs[i]
195 pfxKey, endKey := kv.prefixInterval(cs[i].KeyBytes(), cs[i].RangeEnd)
196 newCmps[i].WithKeyBytes(pfxKey)
197 if len(cs[i].RangeEnd) != 0 {
198 newCmps[i].RangeEnd = endKey
199 }
200 }
201 return newCmps
202}
203
204func (kv *kvPrefix) prefixOps(ops []clientv3.Op) []clientv3.Op {
205 newOps := make([]clientv3.Op, len(ops))
206 for i := range ops {
207 newOps[i] = kv.prefixOp(ops[i])
208 }
209 return newOps
210}