1// Copyright 2016 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 e2e
16
17import (
18	"encoding/json"
19	"fmt"
20	"io"
21	"strings"
22	"testing"
23
24	"github.com/coreos/etcd/etcdserver/etcdserverpb"
25)
26
27func TestCtlV3MemberList(t *testing.T) { testCtl(t, memberListTest) }
28func TestCtlV3MemberRemove(t *testing.T) {
29	testCtl(t, memberRemoveTest, withQuorum(), withNoStrictReconfig())
30}
31func TestCtlV3MemberAdd(t *testing.T)    { testCtl(t, memberAddTest) }
32func TestCtlV3MemberUpdate(t *testing.T) { testCtl(t, memberUpdateTest) }
33
34func memberListTest(cx ctlCtx) {
35	if err := ctlV3MemberList(cx); err != nil {
36		cx.t.Fatalf("memberListTest ctlV3MemberList error (%v)", err)
37	}
38}
39
40func ctlV3MemberList(cx ctlCtx) error {
41	cmdArgs := append(cx.PrefixArgs(), "member", "list")
42	lines := make([]string, cx.cfg.clusterSize)
43	for i := range lines {
44		lines[i] = "started"
45	}
46	return spawnWithExpects(cmdArgs, lines...)
47}
48
49func getMemberList(cx ctlCtx) (etcdserverpb.MemberListResponse, error) {
50	cmdArgs := append(cx.PrefixArgs(), "--write-out", "json", "member", "list")
51
52	proc, err := spawnCmd(cmdArgs)
53	if err != nil {
54		return etcdserverpb.MemberListResponse{}, err
55	}
56	var txt string
57	txt, err = proc.Expect("members")
58	if err != nil {
59		return etcdserverpb.MemberListResponse{}, err
60	}
61	if err = proc.Close(); err != nil {
62		return etcdserverpb.MemberListResponse{}, err
63	}
64
65	resp := etcdserverpb.MemberListResponse{}
66	dec := json.NewDecoder(strings.NewReader(txt))
67	if err := dec.Decode(&resp); err == io.EOF {
68		return etcdserverpb.MemberListResponse{}, err
69	}
70	return resp, nil
71}
72
73func memberRemoveTest(cx ctlCtx) {
74	ep, memIDToRemove, clusterID := cx.memberToRemove()
75	if err := ctlV3MemberRemove(cx, ep, memIDToRemove, clusterID); err != nil {
76		cx.t.Fatal(err)
77	}
78}
79
80func ctlV3MemberRemove(cx ctlCtx, ep, memberID, clusterID string) error {
81	cmdArgs := append(cx.prefixArgs([]string{ep}), "member", "remove", memberID)
82	return spawnWithExpect(cmdArgs, fmt.Sprintf("%s removed from cluster %s", memberID, clusterID))
83}
84
85func memberAddTest(cx ctlCtx) {
86	if err := ctlV3MemberAdd(cx, fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+11)); err != nil {
87		cx.t.Fatal(err)
88	}
89}
90
91func ctlV3MemberAdd(cx ctlCtx, peerURL string) error {
92	cmdArgs := append(cx.PrefixArgs(), "member", "add", "newmember", fmt.Sprintf("--peer-urls=%s", peerURL))
93	return spawnWithExpect(cmdArgs, " added to cluster ")
94}
95
96func memberUpdateTest(cx ctlCtx) {
97	mr, err := getMemberList(cx)
98	if err != nil {
99		cx.t.Fatal(err)
100	}
101
102	peerURL := fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+11)
103	memberID := fmt.Sprintf("%x", mr.Members[0].ID)
104	if err = ctlV3MemberUpdate(cx, memberID, peerURL); err != nil {
105		cx.t.Fatal(err)
106	}
107}
108
109func ctlV3MemberUpdate(cx ctlCtx, memberID, peerURL string) error {
110	cmdArgs := append(cx.PrefixArgs(), "member", "update", memberID, fmt.Sprintf("--peer-urls=%s", peerURL))
111	return spawnWithExpect(cmdArgs, " updated in cluster ")
112}
113