cluster_test.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. package integration
  15. import (
  16. "context"
  17. "fmt"
  18. "reflect"
  19. "strings"
  20. "testing"
  21. "go.etcd.io/etcd/v3/integration"
  22. "go.etcd.io/etcd/v3/pkg/testutil"
  23. "go.etcd.io/etcd/v3/pkg/types"
  24. )
  25. func TestMemberList(t *testing.T) {
  26. defer testutil.AfterTest(t)
  27. clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
  28. defer clus.Terminate(t)
  29. capi := clus.RandClient()
  30. resp, err := capi.MemberList(context.Background())
  31. if err != nil {
  32. t.Fatalf("failed to list member %v", err)
  33. }
  34. if len(resp.Members) != 3 {
  35. t.Errorf("number of members = %d, want %d", len(resp.Members), 3)
  36. }
  37. }
  38. func TestMemberAdd(t *testing.T) {
  39. defer testutil.AfterTest(t)
  40. clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
  41. defer clus.Terminate(t)
  42. capi := clus.RandClient()
  43. urls := []string{"http://127.0.0.1:1234"}
  44. resp, err := capi.MemberAdd(context.Background(), urls)
  45. if err != nil {
  46. t.Fatalf("failed to add member %v", err)
  47. }
  48. if !reflect.DeepEqual(resp.Member.PeerURLs, urls) {
  49. t.Errorf("urls = %v, want %v", urls, resp.Member.PeerURLs)
  50. }
  51. }
  52. func TestMemberAddWithExistingURLs(t *testing.T) {
  53. defer testutil.AfterTest(t)
  54. clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
  55. defer clus.Terminate(t)
  56. capi := clus.RandClient()
  57. resp, err := capi.MemberList(context.Background())
  58. if err != nil {
  59. t.Fatalf("failed to list member %v", err)
  60. }
  61. existingURL := resp.Members[0].PeerURLs[0]
  62. _, err = capi.MemberAdd(context.Background(), []string{existingURL})
  63. expectedErrKeywords := "Peer URLs already exists"
  64. if err == nil {
  65. t.Fatalf("expecting add member to fail, got no error")
  66. }
  67. if !strings.Contains(err.Error(), expectedErrKeywords) {
  68. t.Errorf("expecting error to contain %s, got %s", expectedErrKeywords, err.Error())
  69. }
  70. }
  71. func TestMemberRemove(t *testing.T) {
  72. defer testutil.AfterTest(t)
  73. clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
  74. defer clus.Terminate(t)
  75. capi := clus.Client(1)
  76. resp, err := capi.MemberList(context.Background())
  77. if err != nil {
  78. t.Fatalf("failed to list member %v", err)
  79. }
  80. rmvID := resp.Members[0].ID
  81. // indexes in capi member list don't necessarily match cluster member list;
  82. // find member that is not the client to remove
  83. for _, m := range resp.Members {
  84. mURLs, _ := types.NewURLs(m.PeerURLs)
  85. if !reflect.DeepEqual(mURLs, clus.Members[1].ServerConfig.PeerURLs) {
  86. rmvID = m.ID
  87. break
  88. }
  89. }
  90. _, err = capi.MemberRemove(context.Background(), rmvID)
  91. if err != nil {
  92. t.Fatalf("failed to remove member %v", err)
  93. }
  94. resp, err = capi.MemberList(context.Background())
  95. if err != nil {
  96. t.Fatalf("failed to list member %v", err)
  97. }
  98. if len(resp.Members) != 2 {
  99. t.Errorf("number of members = %d, want %d", len(resp.Members), 2)
  100. }
  101. }
  102. func TestMemberUpdate(t *testing.T) {
  103. defer testutil.AfterTest(t)
  104. clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
  105. defer clus.Terminate(t)
  106. capi := clus.RandClient()
  107. resp, err := capi.MemberList(context.Background())
  108. if err != nil {
  109. t.Fatalf("failed to list member %v", err)
  110. }
  111. urls := []string{"http://127.0.0.1:1234"}
  112. _, err = capi.MemberUpdate(context.Background(), resp.Members[0].ID, urls)
  113. if err != nil {
  114. t.Fatalf("failed to update member %v", err)
  115. }
  116. resp, err = capi.MemberList(context.Background())
  117. if err != nil {
  118. t.Fatalf("failed to list member %v", err)
  119. }
  120. if !reflect.DeepEqual(resp.Members[0].PeerURLs, urls) {
  121. t.Errorf("urls = %v, want %v", urls, resp.Members[0].PeerURLs)
  122. }
  123. }
  124. func TestMemberAddUpdateWrongURLs(t *testing.T) {
  125. defer testutil.AfterTest(t)
  126. clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
  127. defer clus.Terminate(t)
  128. capi := clus.RandClient()
  129. tt := [][]string{
  130. // missing protocol scheme
  131. {"://127.0.0.1:2379"},
  132. // unsupported scheme
  133. {"mailto://127.0.0.1:2379"},
  134. // not conform to host:port
  135. {"http://127.0.0.1"},
  136. // contain a path
  137. {"http://127.0.0.1:2379/path"},
  138. // first path segment in URL cannot contain colon
  139. {"127.0.0.1:1234"},
  140. // URL scheme must be http, https, unix, or unixs
  141. {"localhost:1234"},
  142. }
  143. for i := range tt {
  144. _, err := capi.MemberAdd(context.Background(), tt[i])
  145. if err == nil {
  146. t.Errorf("#%d: MemberAdd err = nil, but error", i)
  147. }
  148. _, err = capi.MemberUpdate(context.Background(), 0, tt[i])
  149. if err == nil {
  150. t.Errorf("#%d: MemberUpdate err = nil, but error", i)
  151. }
  152. }
  153. }
  154. func TestMemberAddForLearner(t *testing.T) {
  155. defer testutil.AfterTest(t)
  156. clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
  157. defer clus.Terminate(t)
  158. capi := clus.RandClient()
  159. urls := []string{"http://127.0.0.1:1234"}
  160. resp, err := capi.MemberAddAsLearner(context.Background(), urls)
  161. if err != nil {
  162. t.Fatalf("failed to add member %v", err)
  163. }
  164. if !resp.Member.IsLearner {
  165. t.Errorf("Added a member as learner, got resp.Member.IsLearner = %v", resp.Member.IsLearner)
  166. }
  167. numOfLearners, err := getNumberOfLearners(clus)
  168. if err != nil {
  169. t.Fatalf("failed to get the number of learners in cluster: %v", err)
  170. }
  171. if numOfLearners != 1 {
  172. t.Errorf("Added 1 learner node to cluster, got %d", numOfLearners)
  173. }
  174. }
  175. // getNumberOfLearners return the number of learner nodes in cluster using MemberList API
  176. func getNumberOfLearners(clus *integration.ClusterV3) (int, error) {
  177. cli := clus.RandClient()
  178. resp, err := cli.MemberList(context.Background())
  179. if err != nil {
  180. return 0, fmt.Errorf("failed to list member %v", err)
  181. }
  182. numberOfLearners := 0
  183. for _, m := range resp.Members {
  184. if m.IsLearner {
  185. numberOfLearners++
  186. }
  187. }
  188. return numberOfLearners, nil
  189. }