mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #15261 from ahrtr/memberlist_20230208
clientv3: support serializable `MemberList` operation
This commit is contained in:
commit
da4bf0f76f
@ -32,6 +32,7 @@ See [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0...v3.6.0).
|
|||||||
- [Always print the raft_term in decimal](https://github.com/etcd-io/etcd/pull/13711) when displaying member list in json.
|
- [Always print the raft_term in decimal](https://github.com/etcd-io/etcd/pull/13711) when displaying member list in json.
|
||||||
- [Add one more field `storageVersion`](https://github.com/etcd-io/etcd/pull/13773) into the response of command `etcdctl endpoint status`.
|
- [Add one more field `storageVersion`](https://github.com/etcd-io/etcd/pull/13773) into the response of command `etcdctl endpoint status`.
|
||||||
- Add [`--max-txn-ops`](https://github.com/etcd-io/etcd/pull/14340) flag to make-mirror command.
|
- Add [`--max-txn-ops`](https://github.com/etcd-io/etcd/pull/14340) flag to make-mirror command.
|
||||||
|
- Add [`--consistency`](https://github.com/etcd-io/etcd/pull/15261) flag to member list command.
|
||||||
- Display [field `hash_revision`](https://github.com/etcd-io/etcd/pull/14812) for `etcdctl endpoint hash` command.
|
- Display [field `hash_revision`](https://github.com/etcd-io/etcd/pull/14812) for `etcdctl endpoint hash` command.
|
||||||
|
|
||||||
### etcdutl v3
|
### etcdutl v3
|
||||||
@ -39,6 +40,10 @@ See [code changes](https://github.com/etcd-io/etcd/compare/v3.5.0...v3.6.0).
|
|||||||
- Add command to generate [shell completion](https://github.com/etcd-io/etcd/pull/13142).
|
- Add command to generate [shell completion](https://github.com/etcd-io/etcd/pull/13142).
|
||||||
- Add `migrate` command for downgrading/upgrading etcd data dir files.
|
- Add `migrate` command for downgrading/upgrading etcd data dir files.
|
||||||
|
|
||||||
|
### Package `clientv3`
|
||||||
|
|
||||||
|
- [Support serializable `MemberList` operation](https://github.com/etcd-io/etcd/pull/15261).
|
||||||
|
|
||||||
### Package `server`
|
### Package `server`
|
||||||
|
|
||||||
- Package `mvcc` was moved to `storage/mvcc`
|
- Package `mvcc` was moved to `storage/mvcc`
|
||||||
|
@ -426,7 +426,7 @@ type mockCluster struct {
|
|||||||
members []*etcdserverpb.Member
|
members []*etcdserverpb.Member
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockCluster) MemberList(ctx context.Context) (*MemberListResponse, error) {
|
func (mc *mockCluster) MemberList(ctx context.Context, opts ...OpOption) (*MemberListResponse, error) {
|
||||||
return &MemberListResponse{Members: mc.members}, nil
|
return &MemberListResponse{Members: mc.members}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ type (
|
|||||||
|
|
||||||
type Cluster interface {
|
type Cluster interface {
|
||||||
// MemberList lists the current cluster membership.
|
// MemberList lists the current cluster membership.
|
||||||
MemberList(ctx context.Context) (*MemberListResponse, error)
|
MemberList(ctx context.Context, opts ...OpOption) (*MemberListResponse, error)
|
||||||
|
|
||||||
// MemberAdd adds a new member into the cluster.
|
// MemberAdd adds a new member into the cluster.
|
||||||
MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
|
MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
|
||||||
@ -122,9 +122,9 @@ func (c *cluster) MemberUpdate(ctx context.Context, id uint64, peerAddrs []strin
|
|||||||
return nil, toErr(ctx, err)
|
return nil, toErr(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cluster) MemberList(ctx context.Context) (*MemberListResponse, error) {
|
func (c *cluster) MemberList(ctx context.Context, opts ...OpOption) (*MemberListResponse, error) {
|
||||||
// it is safe to retry on list.
|
opt := OpGet("", opts...)
|
||||||
resp, err := c.remote.MemberList(ctx, &pb.MemberListRequest{Linearizable: true}, c.callOpts...)
|
resp, err := c.remote.MemberList(ctx, &pb.MemberListRequest{Linearizable: !opt.serializable}, c.callOpts...)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return (*MemberListResponse)(resp), nil
|
return (*MemberListResponse)(resp), nil
|
||||||
}
|
}
|
||||||
|
@ -418,9 +418,15 @@ func WithFromKey() OpOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSerializable makes 'Get' request serializable. By default,
|
// WithSerializable makes `Get` and `MemberList` requests serializable.
|
||||||
// it's linearizable. Serializable requests are better for lower latency
|
// By default, they are linearizable. Serializable requests are better
|
||||||
// requirement.
|
// for lower latency requirement, but users should be aware that they
|
||||||
|
// could get stale data with serializable requests.
|
||||||
|
//
|
||||||
|
// In some situations users may want to use serializable requests. For
|
||||||
|
// example, when adding a new member to a one-node cluster, it's reasonable
|
||||||
|
// and safe to use serializable request before the new added member gets
|
||||||
|
// started.
|
||||||
func WithSerializable() OpOption {
|
func WithSerializable() OpOption {
|
||||||
return func(op *Op) { op.serializable = true }
|
return func(op *Op) { op.serializable = true }
|
||||||
}
|
}
|
||||||
|
@ -119,15 +119,22 @@ RPC: Range
|
|||||||
|
|
||||||
- print-value-only -- print only value when used with write-out=simple
|
- print-value-only -- print only value when used with write-out=simple
|
||||||
|
|
||||||
- consistency -- Linearizable(l) or Serializable(s)
|
- consistency -- Linearizable(l) or Serializable(s), defaults to Linearizable(l).
|
||||||
|
|
||||||
- from-key -- Get keys that are greater than or equal to the given key using byte compare
|
- from-key -- Get keys that are greater than or equal to the given key using byte compare
|
||||||
|
|
||||||
- keys-only -- Get only the keys
|
- keys-only -- Get only the keys
|
||||||
|
|
||||||
#### Output
|
#### Output
|
||||||
|
Prints the data in format below,
|
||||||
|
```
|
||||||
\<key\>\n\<value\>\n\<next_key\>\n\<next_value\>...
|
\<key\>\n\<value\>\n\<next_key\>\n\<next_value\>...
|
||||||
|
```
|
||||||
|
|
||||||
|
Note serializable requests are better for lower latency requirement, but
|
||||||
|
stale data might be returned if serializable option (`--consistency=s`)
|
||||||
|
is specified.
|
||||||
|
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
@ -711,10 +718,19 @@ MEMBER LIST prints the member details for all members associated with an etcd cl
|
|||||||
|
|
||||||
RPC: MemberList
|
RPC: MemberList
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
- consistency -- Linearizable(l) or Serializable(s), defaults to Linearizable(l).
|
||||||
|
|
||||||
#### Output
|
#### Output
|
||||||
|
|
||||||
Prints a humanized table of the member IDs, statuses, names, peer addresses, and client addresses.
|
Prints a humanized table of the member IDs, statuses, names, peer addresses, and client addresses.
|
||||||
|
|
||||||
|
Note serializable requests are better for lower latency requirement, but
|
||||||
|
stale member list might be returned if serializable option (`--consistency=s`)
|
||||||
|
is specified. In some situations users may want to use serializable requests.
|
||||||
|
For example, when adding a new member to a one-node cluster, it's reasonable
|
||||||
|
and safe to use serializable request before the new added member gets started.
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -109,12 +109,8 @@ func getGetOp(args []string) (string, []clientv3.OpOption) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var opts []clientv3.OpOption
|
var opts []clientv3.OpOption
|
||||||
switch getConsistency {
|
if IsSerializable(getConsistency) {
|
||||||
case "s":
|
|
||||||
opts = append(opts, clientv3.WithSerializable())
|
opts = append(opts, clientv3.WithSerializable())
|
||||||
case "l":
|
|
||||||
default:
|
|
||||||
cobrautl.ExitWithError(cobrautl.ExitBadFeature, fmt.Errorf("unknown consistency flag %q", getConsistency))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
key := args[0]
|
key := args[0]
|
||||||
|
@ -27,8 +27,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
memberPeerURLs string
|
memberPeerURLs string
|
||||||
isLearner bool
|
isLearner bool
|
||||||
|
memberConsistency string
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewMemberCommand returns the cobra command for "member".
|
// NewMemberCommand returns the cobra command for "member".
|
||||||
@ -100,6 +101,8 @@ The items in the lists are ID, Status, Name, Peer Addrs, Client Addrs, Is Learne
|
|||||||
Run: memberListCommandFunc,
|
Run: memberListCommandFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cc.Flags().StringVar(&memberConsistency, "consistency", "l", "Linearizable(l) or Serializable(s)")
|
||||||
|
|
||||||
return cc
|
return cc
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,8 +229,12 @@ func memberUpdateCommandFunc(cmd *cobra.Command, args []string) {
|
|||||||
|
|
||||||
// memberListCommandFunc executes the "member list" command.
|
// memberListCommandFunc executes the "member list" command.
|
||||||
func memberListCommandFunc(cmd *cobra.Command, args []string) {
|
func memberListCommandFunc(cmd *cobra.Command, args []string) {
|
||||||
|
var opts []clientv3.OpOption
|
||||||
|
if IsSerializable(memberConsistency) {
|
||||||
|
opts = append(opts, clientv3.WithSerializable())
|
||||||
|
}
|
||||||
ctx, cancel := commandCtx(cmd)
|
ctx, cancel := commandCtx(cmd)
|
||||||
resp, err := mustClientFromCmd(cmd).MemberList(ctx)
|
resp, err := mustClientFromCmd(cmd).MemberList(ctx, opts...)
|
||||||
cancel()
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cobrautl.ExitWithError(cobrautl.ExitError, err)
|
cobrautl.ExitWithError(cobrautl.ExitError, err)
|
||||||
|
@ -166,3 +166,14 @@ func defrag(c *clientv3.Client, ep string) {
|
|||||||
}
|
}
|
||||||
fmt.Printf("Defragmented %q\n", ep)
|
fmt.Printf("Defragmented %q\n", ep)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsSerializable(option string) bool {
|
||||||
|
switch option {
|
||||||
|
case "s":
|
||||||
|
return true
|
||||||
|
case "l":
|
||||||
|
default:
|
||||||
|
cobrautl.ExitWithError(cobrautl.ExitBadFeature, fmt.Errorf("unknown consistency flag %q", getConsistency))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -40,7 +40,7 @@ func TestMemberList(t *testing.T) {
|
|||||||
cc := testutils.MustClient(clus.Client())
|
cc := testutils.MustClient(clus.Client())
|
||||||
|
|
||||||
testutils.ExecuteUntil(ctx, t, func() {
|
testutils.ExecuteUntil(ctx, t, func() {
|
||||||
resp, err := cc.MemberList(ctx)
|
resp, err := cc.MemberList(ctx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not get member list, err: %s", err)
|
t.Fatalf("could not get member list, err: %s", err)
|
||||||
}
|
}
|
||||||
@ -237,7 +237,7 @@ func TestMemberRemove(t *testing.T) {
|
|||||||
// Otherwise, return a member that client has not connected to.
|
// Otherwise, return a member that client has not connected to.
|
||||||
// It ensures that `MemberRemove` function does not return an "etcdserver: server stopped" error.
|
// It ensures that `MemberRemove` function does not return an "etcdserver: server stopped" error.
|
||||||
func memberToRemove(ctx context.Context, t *testing.T, client intf.Client, clusterSize int) (memberId uint64, clusterId uint64) {
|
func memberToRemove(ctx context.Context, t *testing.T, client intf.Client, clusterSize int) (memberId uint64, clusterId uint64) {
|
||||||
listResp, err := client.MemberList(ctx)
|
listResp, err := client.MemberList(ctx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ func TestPeriodicCheckDetectsCorruption(t *testing.T) {
|
|||||||
assert.NoError(t, err, "error on put")
|
assert.NoError(t, err, "error on put")
|
||||||
}
|
}
|
||||||
|
|
||||||
members, err := cc.MemberList(ctx)
|
members, err := cc.MemberList(ctx, false)
|
||||||
assert.NoError(t, err, "error on member list")
|
assert.NoError(t, err, "error on member list")
|
||||||
var memberID uint64
|
var memberID uint64
|
||||||
for _, m := range members.Members {
|
for _, m := range members.Members {
|
||||||
@ -171,7 +171,7 @@ func TestCompactHashCheckDetectCorruption(t *testing.T) {
|
|||||||
err := cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i), config.PutOptions{})
|
err := cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i), config.PutOptions{})
|
||||||
assert.NoError(t, err, "error on put")
|
assert.NoError(t, err, "error on put")
|
||||||
}
|
}
|
||||||
members, err := cc.MemberList(ctx)
|
members, err := cc.MemberList(ctx, false)
|
||||||
assert.NoError(t, err, "error on member list")
|
assert.NoError(t, err, "error on member list")
|
||||||
var memberID uint64
|
var memberID uint64
|
||||||
for _, m := range members.Members {
|
for _, m := range members.Members {
|
||||||
|
@ -157,7 +157,7 @@ func authTestMemberUpdate(cx ctlCtx) {
|
|||||||
cx.user, cx.pass = "root", "root"
|
cx.user, cx.pass = "root", "root"
|
||||||
authSetupTestUser(cx)
|
authSetupTestUser(cx)
|
||||||
|
|
||||||
mr, err := getMemberList(cx)
|
mr, err := getMemberList(cx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cx.t.Fatal(err)
|
cx.t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,20 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.etcd.io/etcd/api/v3/etcdserverpb"
|
"go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||||
"go.etcd.io/etcd/tests/v3/framework/e2e"
|
"go.etcd.io/etcd/tests/v3/framework/e2e"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCtlV3MemberList(t *testing.T) { testCtl(t, memberListTest) }
|
func TestCtlV3MemberList(t *testing.T) { testCtl(t, memberListTest) }
|
||||||
func TestCtlV3MemberListWithHex(t *testing.T) { testCtl(t, memberListWithHexTest) }
|
func TestCtlV3MemberListWithHex(t *testing.T) { testCtl(t, memberListWithHexTest) }
|
||||||
|
func TestCtlV3MemberListSerializable(t *testing.T) {
|
||||||
|
cfg := e2e.NewConfig(
|
||||||
|
e2e.WithClusterSize(1),
|
||||||
|
)
|
||||||
|
testCtl(t, memberListSerializableTest, withCfg(*cfg))
|
||||||
|
}
|
||||||
|
|
||||||
func TestCtlV3MemberAdd(t *testing.T) { testCtl(t, memberAddTest) }
|
func TestCtlV3MemberAdd(t *testing.T) { testCtl(t, memberAddTest) }
|
||||||
func TestCtlV3MemberAddAsLearner(t *testing.T) { testCtl(t, memberAddAsLearnerTest) }
|
func TestCtlV3MemberAddAsLearner(t *testing.T) { testCtl(t, memberAddAsLearnerTest) }
|
||||||
@ -52,6 +60,19 @@ func memberListTest(cx ctlCtx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func memberListSerializableTest(cx ctlCtx) {
|
||||||
|
resp, err := getMemberList(cx, false)
|
||||||
|
require.NoError(cx.t, err)
|
||||||
|
require.Equal(cx.t, 1, len(resp.Members))
|
||||||
|
|
||||||
|
peerURL := fmt.Sprintf("http://localhost:%d", e2e.EtcdProcessBasePort+11)
|
||||||
|
err = ctlV3MemberAdd(cx, peerURL, false)
|
||||||
|
require.NoError(cx.t, err)
|
||||||
|
|
||||||
|
resp, err = getMemberList(cx, true)
|
||||||
|
require.Equal(cx.t, 2, len(resp.Members))
|
||||||
|
}
|
||||||
|
|
||||||
func ctlV3MemberList(cx ctlCtx) error {
|
func ctlV3MemberList(cx ctlCtx) error {
|
||||||
cmdArgs := append(cx.PrefixArgs(), "member", "list")
|
cmdArgs := append(cx.PrefixArgs(), "member", "list")
|
||||||
lines := make([]string, cx.cfg.ClusterSize)
|
lines := make([]string, cx.cfg.ClusterSize)
|
||||||
@ -61,8 +82,11 @@ func ctlV3MemberList(cx ctlCtx) error {
|
|||||||
return e2e.SpawnWithExpects(cmdArgs, cx.envMap, lines...)
|
return e2e.SpawnWithExpects(cmdArgs, cx.envMap, lines...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMemberList(cx ctlCtx) (etcdserverpb.MemberListResponse, error) {
|
func getMemberList(cx ctlCtx, serializable bool) (etcdserverpb.MemberListResponse, error) {
|
||||||
cmdArgs := append(cx.PrefixArgs(), "--write-out", "json", "member", "list")
|
cmdArgs := append(cx.PrefixArgs(), "--write-out", "json", "member", "list")
|
||||||
|
if serializable {
|
||||||
|
cmdArgs = append(cmdArgs, "--consistency", "s")
|
||||||
|
}
|
||||||
|
|
||||||
proc, err := e2e.SpawnCmd(cmdArgs, cx.envMap)
|
proc, err := e2e.SpawnCmd(cmdArgs, cx.envMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -86,7 +110,7 @@ func getMemberList(cx ctlCtx) (etcdserverpb.MemberListResponse, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func memberListWithHexTest(cx ctlCtx) {
|
func memberListWithHexTest(cx ctlCtx) {
|
||||||
resp, err := getMemberList(cx)
|
resp, err := getMemberList(cx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cx.t.Fatalf("getMemberList error (%v)", err)
|
cx.t.Fatalf("getMemberList error (%v)", err)
|
||||||
}
|
}
|
||||||
@ -166,7 +190,7 @@ func ctlV3MemberAdd(cx ctlCtx, peerURL string, isLearner bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func memberUpdateTest(cx ctlCtx) {
|
func memberUpdateTest(cx ctlCtx) {
|
||||||
mr, err := getMemberList(cx)
|
mr, err := getMemberList(cx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cx.t.Fatal(err)
|
cx.t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -350,7 +350,7 @@ func (cx *ctlCtx) memberToRemove() (ep string, memberID string, clusterID string
|
|||||||
cx.t.Fatalf("%d-node is too small to test 'member remove'", n1)
|
cx.t.Fatalf("%d-node is too small to test 'member remove'", n1)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := getMemberList(*cx)
|
resp, err := getMemberList(*cx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cx.t.Fatal(err)
|
cx.t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ func TestV2DeprecationSnapshotRecover(t *testing.T) {
|
|||||||
lastReleaseGetResponse, err := cc.Get(ctx, "", config.GetOptions{Prefix: true})
|
lastReleaseGetResponse, err := cc.Get(ctx, "", config.GetOptions{Prefix: true})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
lastReleaseMemberListResponse, err := cc.MemberList(ctx)
|
lastReleaseMemberListResponse, err := cc.MemberList(ctx, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.NoError(t, epc.Close())
|
assert.NoError(t, epc.Close())
|
||||||
@ -174,7 +174,7 @@ func TestV2DeprecationSnapshotRecover(t *testing.T) {
|
|||||||
currentReleaseGetResponse, err := cc.Get(ctx, "", config.GetOptions{Prefix: true})
|
currentReleaseGetResponse, err := cc.Get(ctx, "", config.GetOptions{Prefix: true})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
currentReleaseMemberListResponse, err := cc.MemberList(ctx)
|
currentReleaseMemberListResponse, err := cc.MemberList(ctx, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, lastReleaseGetResponse.Kvs, currentReleaseGetResponse.Kvs)
|
assert.Equal(t, lastReleaseGetResponse.Kvs, currentReleaseGetResponse.Kvs)
|
||||||
|
@ -721,7 +721,7 @@ func (epc *EtcdProcessCluster) CloseProc(ctx context.Context, finder func(EtcdPr
|
|||||||
// First remove member from the cluster
|
// First remove member from the cluster
|
||||||
|
|
||||||
memberCtl := epc.Client(opts...)
|
memberCtl := epc.Client(opts...)
|
||||||
memberList, err := memberCtl.MemberList(ctx)
|
memberList, err := memberCtl.MemberList(ctx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get member list: %w", err)
|
return fmt.Errorf("failed to get member list: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -274,9 +274,13 @@ func AddTxnResponse(resp *clientv3.TxnResponse, jsonData string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctl *EtcdctlV3) MemberList(ctx context.Context) (*clientv3.MemberListResponse, error) {
|
func (ctl *EtcdctlV3) MemberList(ctx context.Context, serializable bool) (*clientv3.MemberListResponse, error) {
|
||||||
var resp clientv3.MemberListResponse
|
var resp clientv3.MemberListResponse
|
||||||
err := ctl.spawnJsonCmd(ctx, &resp, "member", "list")
|
args := []string{"member", "list"}
|
||||||
|
if serializable {
|
||||||
|
args = append(args, "--consistency", "s")
|
||||||
|
}
|
||||||
|
err := ctl.spawnJsonCmd(ctx, &resp, args...)
|
||||||
return &resp, err
|
return &resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,3 +418,10 @@ func (c integrationClient) MemberAddAsLearner(ctx context.Context, _ string, pee
|
|||||||
func (c integrationClient) MemberRemove(ctx context.Context, id uint64) (*clientv3.MemberRemoveResponse, error) {
|
func (c integrationClient) MemberRemove(ctx context.Context, id uint64) (*clientv3.MemberRemoveResponse, error) {
|
||||||
return c.Client.MemberRemove(ctx, id)
|
return c.Client.MemberRemove(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c integrationClient) MemberList(ctx context.Context, serializable bool) (*clientv3.MemberListResponse, error) {
|
||||||
|
if serializable {
|
||||||
|
return c.Client.MemberList(ctx, clientv3.WithSerializable())
|
||||||
|
}
|
||||||
|
return c.Client.MemberList(ctx)
|
||||||
|
}
|
||||||
|
@ -78,7 +78,7 @@ type Client interface {
|
|||||||
|
|
||||||
Txn(context context.Context, compares, ifSucess, ifFail []string, o config.TxnOptions) (*clientv3.TxnResponse, error)
|
Txn(context context.Context, compares, ifSucess, ifFail []string, o config.TxnOptions) (*clientv3.TxnResponse, error)
|
||||||
|
|
||||||
MemberList(context context.Context) (*clientv3.MemberListResponse, error)
|
MemberList(context context.Context, serializable bool) (*clientv3.MemberListResponse, error)
|
||||||
MemberAdd(context context.Context, name string, peerAddrs []string) (*clientv3.MemberAddResponse, error)
|
MemberAdd(context context.Context, name string, peerAddrs []string) (*clientv3.MemberAddResponse, error)
|
||||||
MemberAddAsLearner(context context.Context, name string, peerAddrs []string) (*clientv3.MemberAddResponse, error)
|
MemberAddAsLearner(context context.Context, name string, peerAddrs []string) (*clientv3.MemberAddResponse, error)
|
||||||
MemberRemove(ctx context.Context, id uint64) (*clientv3.MemberRemoveResponse, error)
|
MemberRemove(ctx context.Context, id uint64) (*clientv3.MemberRemoveResponse, error)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user