Repository: etcd-io/etcd
File: client/v3/kubernetes/client.go (Go)

Code before:
)

// New creates Client from config.
// Caller is responsible to call Close() to clean up client.
func New(cfg clientv3.Config) (*Client, error) {
	c, err := clientv3.New(cfg)
	if err != nil {
		return nil, err
	}
	kc := &Client{
		Client: c,
		kv:     clientv3.RetryKVClient(c),
	}
	kc.callOpts, err = clientv3.Options(&cfg)
	if err != nil {
		return nil, err
	}
	kc.Kubernetes = kc
	return kc, nil
}

type Client struct {
	*clientv3.Client
	Kubernetes Interface
	kv         pb.KVClient
	callOpts   []grpc.CallOption
}

var _ Interface = (*Client)(nil)

func (k Client) Get(ctx context.Context, key string, opts GetOptions) (resp GetResponse, err error) {
	rangeResp, err := k.kv.Range(ctx, getRequest(key, opts.Revision), k.callOpts...)
	if err != nil {
		return resp, clientv3.ContextError(ctx, err)
	}
	resp.Revision = rangeResp.Header.Revision
	if len(rangeResp.Kvs) == 1 {
		resp.KV = rangeResp.Kvs[0]
	}
	return resp, nil
}

func (k Client) List(ctx context.Context, prefix string, opts ListOptions) (resp ListResponse, err error) {
	rangeStart := prefix + opts.Continue
	rangeEnd := clientv3.GetPrefixRangeEnd(prefix)

	rangeResp, err := k.kv.Range(ctx, &pb.RangeRequest{
		Key:      []byte(rangeStart),
		RangeEnd: []byte(rangeEnd),
		Limit:    opts.Limit,
		Revision: opts.Revision,

Review comment (suggestion):
It seems that it's better to reuse [clientv3.KV](https://github.com/etcd-io/etcd/blob/9a6c9aeeabe1cac50623f4933674d5dd258a866f/client/v3/kv.go#L34).

https://github.com/etcd-io/etcd/blob/9a6c9aeeabe1cac50623f4933674d5dd258a866f/client/v3/kv.go#L93-L104

You can make change something like below. But you also need to make further change because `clientv3.KV` has slight different interface methods as `pb.KVClient`.

```
$ git diff
diff --git a/client/v3/kubernetes/client.go b/client/v3/kubernetes/client.go
index 4ca0e5fe8..67a5b50cf 100644
--- a/client/v3/kubernetes/client.go
+++ b/client/v3/kubernetes/client.go
@@ -31,7 +31,7 @@ func New(cfg clientv3.Config) (*Client, error) {
        }
        kc := &Client{
                Client: c,
-               kv:     clientv3.RetryKVClient(c),
+               kv:     clientv3.NewKV(c),
        }
        kc.Kubernetes = kc
        return kc, nil
@@ -40,7 +40,7 @@ func New(cfg clientv3.Config) (*Client, error) {
 type Client struct {
        *clientv3.Client
        Kubernetes Interface
-       kv         pb.KVClient
+       kv         clientv3.KV
 }
 
 var _ Interface = (*Client)(nil)
```

Code after:

// New creates Client from config.
// Caller is responsible to call Close() to clean up client.
func New(cfg clientv3.Config) (*Client, error) {
	c, err := clientv3.New(cfg)
	if err != nil {
		return nil, err
	}
	kc := &Client{
		Client: c,
	}
	kc.Kubernetes = kc
	return kc, nil
}

type Client struct {
	*clientv3.Client
	Kubernetes Interface
}

var _ Interface = (*Client)(nil)

func (k Client) Get(ctx context.Context, key string, opts GetOptions) (resp GetResponse, err error) {
	rangeResp, err := k.KV.Get(ctx, key, clientv3.WithRev(opts.Revision), clientv3.WithLimit(1))
	if err != nil {
		return resp, err
	}
	resp.Revision = rangeResp.Header.Revision
	if len(rangeResp.Kvs) == 1 {
		resp.KV = rangeResp.Kvs[0]
	}
	return resp, nil
}

func (k Client) List(ctx context.Context, prefix string, opts ListOptions) (resp ListResponse, err error) {
	rangeStart := prefix
	if opts.Continue != "" {
		rangeStart = opts.Continue
	}
	rangeEnd := clientv3.GetPrefixRangeEnd(prefix)
	rangeResp, err := k.KV.Get(ctx, rangeStart, clientv3.WithRange(rangeEnd), clientv3.WithLimit(opts.Limit), clientv3.WithRev(opts.Revision))
	if err != nil {
		return resp, err
	}
	resp.Kvs = rangeResp.Kvs
	resp.Count = rangeResp.Count
	resp.Revision = rangeResp.Header.Revision
	return resp, nil
}

func (k Client) Count(ctx context.Context, prefix string, _ CountOptions) (int64, error) {