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 integration
16
17import (
18	"reflect"
19	"testing"
20
21	"github.com/coreos/etcd/integration"
22	"github.com/coreos/etcd/pkg/testutil"
23	"github.com/coreos/etcd/pkg/types"
24
25	"golang.org/x/net/context"
26)
27
28func TestMemberList(t *testing.T) {
29	defer testutil.AfterTest(t)
30
31	clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
32	defer clus.Terminate(t)
33
34	capi := clus.RandClient()
35
36	resp, err := capi.MemberList(context.Background())
37	if err != nil {
38		t.Fatalf("failed to list member %v", err)
39	}
40
41	if len(resp.Members) != 3 {
42		t.Errorf("number of members = %d, want %d", len(resp.Members), 3)
43	}
44}
45
46func TestMemberAdd(t *testing.T) {
47	defer testutil.AfterTest(t)
48
49	clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
50	defer clus.Terminate(t)
51
52	capi := clus.RandClient()
53
54	urls := []string{"http://127.0.0.1:1234"}
55	resp, err := capi.MemberAdd(context.Background(), urls)
56	if err != nil {
57		t.Fatalf("failed to add member %v", err)
58	}
59
60	if !reflect.DeepEqual(resp.Member.PeerURLs, urls) {
61		t.Errorf("urls = %v, want %v", urls, resp.Member.PeerURLs)
62	}
63}
64
65func TestMemberRemove(t *testing.T) {
66	defer testutil.AfterTest(t)
67
68	clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
69	defer clus.Terminate(t)
70
71	capi := clus.Client(1)
72	resp, err := capi.MemberList(context.Background())
73	if err != nil {
74		t.Fatalf("failed to list member %v", err)
75	}
76
77	rmvID := resp.Members[0].ID
78	// indexes in capi member list don't necessarily match cluster member list;
79	// find member that is not the client to remove
80	for _, m := range resp.Members {
81		mURLs, _ := types.NewURLs(m.PeerURLs)
82		if !reflect.DeepEqual(mURLs, clus.Members[1].ServerConfig.PeerURLs) {
83			rmvID = m.ID
84			break
85		}
86	}
87
88	_, err = capi.MemberRemove(context.Background(), rmvID)
89	if err != nil {
90		t.Fatalf("failed to remove member %v", err)
91	}
92
93	resp, err = capi.MemberList(context.Background())
94	if err != nil {
95		t.Fatalf("failed to list member %v", err)
96	}
97
98	if len(resp.Members) != 2 {
99		t.Errorf("number of members = %d, want %d", len(resp.Members), 2)
100	}
101}
102
103func TestMemberUpdate(t *testing.T) {
104	defer testutil.AfterTest(t)
105
106	clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
107	defer clus.Terminate(t)
108
109	capi := clus.RandClient()
110	resp, err := capi.MemberList(context.Background())
111	if err != nil {
112		t.Fatalf("failed to list member %v", err)
113	}
114
115	urls := []string{"http://127.0.0.1:1234"}
116	_, err = capi.MemberUpdate(context.Background(), resp.Members[0].ID, urls)
117	if err != nil {
118		t.Fatalf("failed to update member %v", err)
119	}
120
121	resp, err = capi.MemberList(context.Background())
122	if err != nil {
123		t.Fatalf("failed to list member %v", err)
124	}
125
126	if !reflect.DeepEqual(resp.Members[0].PeerURLs, urls) {
127		t.Errorf("urls = %v, want %v", urls, resp.Members[0].PeerURLs)
128	}
129}
130
131func TestMemberAddUpdateWrongURLs(t *testing.T) {
132	defer testutil.AfterTest(t)
133
134	clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
135	defer clus.Terminate(t)
136
137	capi := clus.RandClient()
138	tt := [][]string{
139		// missing protocol scheme
140		{"://127.0.0.1:2379"},
141		// unsupported scheme
142		{"mailto://127.0.0.1:2379"},
143		// not conform to host:port
144		{"http://127.0.0.1"},
145		// contain a path
146		{"http://127.0.0.1:2379/path"},
147		// first path segment in URL cannot contain colon
148		{"127.0.0.1:1234"},
149		// URL scheme must be http, https, unix, or unixs
150		{"localhost:1234"},
151	}
152	for i := range tt {
153		_, err := capi.MemberAdd(context.Background(), tt[i])
154		if err == nil {
155			t.Errorf("#%d: MemberAdd err = nil, but error", i)
156		}
157		_, err = capi.MemberUpdate(context.Background(), 0, tt[i])
158		if err == nil {
159			t.Errorf("#%d: MemberUpdate err = nil, but error", i)
160		}
161	}
162}
163