cluster_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. // Copyright 2015 CoreOS, Inc.
  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 etcdserver
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "path"
  19. "reflect"
  20. "testing"
  21. "github.com/coreos/etcd/pkg/testutil"
  22. "github.com/coreos/etcd/pkg/types"
  23. "github.com/coreos/etcd/raft/raftpb"
  24. "github.com/coreos/etcd/store"
  25. )
  26. func TestClusterMember(t *testing.T) {
  27. membs := []*Member{
  28. newTestMember(1, nil, "node1", nil),
  29. newTestMember(2, nil, "node2", nil),
  30. }
  31. tests := []struct {
  32. id types.ID
  33. match bool
  34. }{
  35. {1, true},
  36. {2, true},
  37. {3, false},
  38. }
  39. for i, tt := range tests {
  40. c := newTestCluster(membs)
  41. m := c.Member(tt.id)
  42. if g := m != nil; g != tt.match {
  43. t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
  44. }
  45. if m != nil && m.ID != tt.id {
  46. t.Errorf("#%d: id = %x, want %x", i, m.ID, tt.id)
  47. }
  48. }
  49. }
  50. func TestClusterMemberByName(t *testing.T) {
  51. membs := []*Member{
  52. newTestMember(1, nil, "node1", nil),
  53. newTestMember(2, nil, "node2", nil),
  54. }
  55. tests := []struct {
  56. name string
  57. match bool
  58. }{
  59. {"node1", true},
  60. {"node2", true},
  61. {"node3", false},
  62. }
  63. for i, tt := range tests {
  64. c := newTestCluster(membs)
  65. m := c.MemberByName(tt.name)
  66. if g := m != nil; g != tt.match {
  67. t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
  68. }
  69. if m != nil && m.Name != tt.name {
  70. t.Errorf("#%d: name = %v, want %v", i, m.Name, tt.name)
  71. }
  72. }
  73. }
  74. func TestClusterMemberIDs(t *testing.T) {
  75. c := newTestCluster([]*Member{
  76. newTestMember(1, nil, "", nil),
  77. newTestMember(4, nil, "", nil),
  78. newTestMember(100, nil, "", nil),
  79. })
  80. w := []types.ID{1, 4, 100}
  81. g := c.MemberIDs()
  82. if !reflect.DeepEqual(w, g) {
  83. t.Errorf("IDs = %+v, want %+v", g, w)
  84. }
  85. }
  86. func TestClusterPeerURLs(t *testing.T) {
  87. tests := []struct {
  88. mems []*Member
  89. wurls []string
  90. }{
  91. // single peer with a single address
  92. {
  93. mems: []*Member{
  94. newTestMember(1, []string{"http://192.0.2.1"}, "", nil),
  95. },
  96. wurls: []string{"http://192.0.2.1"},
  97. },
  98. // single peer with a single address with a port
  99. {
  100. mems: []*Member{
  101. newTestMember(1, []string{"http://192.0.2.1:8001"}, "", nil),
  102. },
  103. wurls: []string{"http://192.0.2.1:8001"},
  104. },
  105. // several members explicitly unsorted
  106. {
  107. mems: []*Member{
  108. newTestMember(2, []string{"http://192.0.2.3", "http://192.0.2.4"}, "", nil),
  109. newTestMember(3, []string{"http://192.0.2.5", "http://192.0.2.6"}, "", nil),
  110. newTestMember(1, []string{"http://192.0.2.1", "http://192.0.2.2"}, "", nil),
  111. },
  112. wurls: []string{"http://192.0.2.1", "http://192.0.2.2", "http://192.0.2.3", "http://192.0.2.4", "http://192.0.2.5", "http://192.0.2.6"},
  113. },
  114. // no members
  115. {
  116. mems: []*Member{},
  117. wurls: []string{},
  118. },
  119. // peer with no peer urls
  120. {
  121. mems: []*Member{
  122. newTestMember(3, []string{}, "", nil),
  123. },
  124. wurls: []string{},
  125. },
  126. }
  127. for i, tt := range tests {
  128. c := newTestCluster(tt.mems)
  129. urls := c.PeerURLs()
  130. if !reflect.DeepEqual(urls, tt.wurls) {
  131. t.Errorf("#%d: PeerURLs = %v, want %v", i, urls, tt.wurls)
  132. }
  133. }
  134. }
  135. func TestClusterClientURLs(t *testing.T) {
  136. tests := []struct {
  137. mems []*Member
  138. wurls []string
  139. }{
  140. // single peer with a single address
  141. {
  142. mems: []*Member{
  143. newTestMember(1, nil, "", []string{"http://192.0.2.1"}),
  144. },
  145. wurls: []string{"http://192.0.2.1"},
  146. },
  147. // single peer with a single address with a port
  148. {
  149. mems: []*Member{
  150. newTestMember(1, nil, "", []string{"http://192.0.2.1:8001"}),
  151. },
  152. wurls: []string{"http://192.0.2.1:8001"},
  153. },
  154. // several members explicitly unsorted
  155. {
  156. mems: []*Member{
  157. newTestMember(2, nil, "", []string{"http://192.0.2.3", "http://192.0.2.4"}),
  158. newTestMember(3, nil, "", []string{"http://192.0.2.5", "http://192.0.2.6"}),
  159. newTestMember(1, nil, "", []string{"http://192.0.2.1", "http://192.0.2.2"}),
  160. },
  161. wurls: []string{"http://192.0.2.1", "http://192.0.2.2", "http://192.0.2.3", "http://192.0.2.4", "http://192.0.2.5", "http://192.0.2.6"},
  162. },
  163. // no members
  164. {
  165. mems: []*Member{},
  166. wurls: []string{},
  167. },
  168. // peer with no client urls
  169. {
  170. mems: []*Member{
  171. newTestMember(3, nil, "", []string{}),
  172. },
  173. wurls: []string{},
  174. },
  175. }
  176. for i, tt := range tests {
  177. c := newTestCluster(tt.mems)
  178. urls := c.ClientURLs()
  179. if !reflect.DeepEqual(urls, tt.wurls) {
  180. t.Errorf("#%d: ClientURLs = %v, want %v", i, urls, tt.wurls)
  181. }
  182. }
  183. }
  184. func TestClusterValidateAndAssignIDsBad(t *testing.T) {
  185. tests := []struct {
  186. clmembs []*Member
  187. membs []*Member
  188. }{
  189. {
  190. // unmatched length
  191. []*Member{
  192. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  193. },
  194. []*Member{},
  195. },
  196. {
  197. // unmatched peer urls
  198. []*Member{
  199. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  200. },
  201. []*Member{
  202. newTestMember(1, []string{"http://127.0.0.1:4001"}, "", nil),
  203. },
  204. },
  205. {
  206. // unmatched peer urls
  207. []*Member{
  208. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  209. newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
  210. },
  211. []*Member{
  212. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  213. newTestMember(2, []string{"http://127.0.0.2:4001"}, "", nil),
  214. },
  215. },
  216. }
  217. for i, tt := range tests {
  218. ecl := newTestCluster(tt.clmembs)
  219. lcl := newTestCluster(tt.membs)
  220. if err := ValidateClusterAndAssignIDs(lcl, ecl); err == nil {
  221. t.Errorf("#%d: unexpected update success", i)
  222. }
  223. }
  224. }
  225. func TestClusterValidateAndAssignIDs(t *testing.T) {
  226. tests := []struct {
  227. clmembs []*Member
  228. membs []*Member
  229. wids []types.ID
  230. }{
  231. {
  232. []*Member{
  233. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  234. newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
  235. },
  236. []*Member{
  237. newTestMember(3, []string{"http://127.0.0.1:2379"}, "", nil),
  238. newTestMember(4, []string{"http://127.0.0.2:2379"}, "", nil),
  239. },
  240. []types.ID{3, 4},
  241. },
  242. }
  243. for i, tt := range tests {
  244. lcl := newTestCluster(tt.clmembs)
  245. ecl := newTestCluster(tt.membs)
  246. if err := ValidateClusterAndAssignIDs(lcl, ecl); err != nil {
  247. t.Errorf("#%d: unexpect update error: %v", i, err)
  248. }
  249. if !reflect.DeepEqual(lcl.MemberIDs(), tt.wids) {
  250. t.Errorf("#%d: ids = %v, want %v", i, lcl.MemberIDs(), tt.wids)
  251. }
  252. }
  253. }
  254. func TestClusterValidateConfigurationChange(t *testing.T) {
  255. cl := newCluster("")
  256. cl.SetStore(store.New())
  257. for i := 1; i <= 4; i++ {
  258. attr := RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", i)}}
  259. cl.AddMember(&Member{ID: types.ID(i), RaftAttributes: attr})
  260. }
  261. cl.RemoveMember(4)
  262. attr := RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 1)}}
  263. ctx, err := json.Marshal(&Member{ID: types.ID(5), RaftAttributes: attr})
  264. if err != nil {
  265. t.Fatal(err)
  266. }
  267. attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 5)}}
  268. ctx5, err := json.Marshal(&Member{ID: types.ID(5), RaftAttributes: attr})
  269. if err != nil {
  270. t.Fatal(err)
  271. }
  272. attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 3)}}
  273. ctx2to3, err := json.Marshal(&Member{ID: types.ID(2), RaftAttributes: attr})
  274. if err != nil {
  275. t.Fatal(err)
  276. }
  277. attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 5)}}
  278. ctx2to5, err := json.Marshal(&Member{ID: types.ID(2), RaftAttributes: attr})
  279. if err != nil {
  280. t.Fatal(err)
  281. }
  282. tests := []struct {
  283. cc raftpb.ConfChange
  284. werr error
  285. }{
  286. {
  287. raftpb.ConfChange{
  288. Type: raftpb.ConfChangeRemoveNode,
  289. NodeID: 3,
  290. },
  291. nil,
  292. },
  293. {
  294. raftpb.ConfChange{
  295. Type: raftpb.ConfChangeAddNode,
  296. NodeID: 4,
  297. },
  298. ErrIDRemoved,
  299. },
  300. {
  301. raftpb.ConfChange{
  302. Type: raftpb.ConfChangeRemoveNode,
  303. NodeID: 4,
  304. },
  305. ErrIDRemoved,
  306. },
  307. {
  308. raftpb.ConfChange{
  309. Type: raftpb.ConfChangeAddNode,
  310. NodeID: 1,
  311. },
  312. ErrIDExists,
  313. },
  314. {
  315. raftpb.ConfChange{
  316. Type: raftpb.ConfChangeAddNode,
  317. NodeID: 5,
  318. Context: ctx,
  319. },
  320. ErrPeerURLexists,
  321. },
  322. {
  323. raftpb.ConfChange{
  324. Type: raftpb.ConfChangeRemoveNode,
  325. NodeID: 5,
  326. },
  327. ErrIDNotFound,
  328. },
  329. {
  330. raftpb.ConfChange{
  331. Type: raftpb.ConfChangeAddNode,
  332. NodeID: 5,
  333. Context: ctx5,
  334. },
  335. nil,
  336. },
  337. {
  338. raftpb.ConfChange{
  339. Type: raftpb.ConfChangeUpdateNode,
  340. NodeID: 5,
  341. Context: ctx,
  342. },
  343. ErrIDNotFound,
  344. },
  345. // try to change the peer url of 2 to the peer url of 3
  346. {
  347. raftpb.ConfChange{
  348. Type: raftpb.ConfChangeUpdateNode,
  349. NodeID: 2,
  350. Context: ctx2to3,
  351. },
  352. ErrPeerURLexists,
  353. },
  354. {
  355. raftpb.ConfChange{
  356. Type: raftpb.ConfChangeUpdateNode,
  357. NodeID: 2,
  358. Context: ctx2to5,
  359. },
  360. nil,
  361. },
  362. }
  363. for i, tt := range tests {
  364. err := cl.ValidateConfigurationChange(tt.cc)
  365. if err != tt.werr {
  366. t.Errorf("#%d: validateConfigurationChange error = %v, want %v", i, err, tt.werr)
  367. }
  368. }
  369. }
  370. func TestClusterGenID(t *testing.T) {
  371. cs := newTestCluster([]*Member{
  372. newTestMember(1, nil, "", nil),
  373. newTestMember(2, nil, "", nil),
  374. })
  375. cs.genID()
  376. if cs.ID() == 0 {
  377. t.Fatalf("cluster.ID = %v, want not 0", cs.ID())
  378. }
  379. previd := cs.ID()
  380. cs.SetStore(&storeRecorder{})
  381. cs.AddMember(newTestMember(3, nil, "", nil))
  382. cs.genID()
  383. if cs.ID() == previd {
  384. t.Fatalf("cluster.ID = %v, want not %v", cs.ID(), previd)
  385. }
  386. }
  387. func TestNodeToMemberBad(t *testing.T) {
  388. tests := []*store.NodeExtern{
  389. {Key: "/1234", Nodes: []*store.NodeExtern{
  390. {Key: "/1234/strange"},
  391. }},
  392. {Key: "/1234", Nodes: []*store.NodeExtern{
  393. {Key: "/1234/raftAttributes", Value: stringp("garbage")},
  394. }},
  395. {Key: "/1234", Nodes: []*store.NodeExtern{
  396. {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  397. }},
  398. {Key: "/1234", Nodes: []*store.NodeExtern{
  399. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  400. {Key: "/1234/strange"},
  401. }},
  402. {Key: "/1234", Nodes: []*store.NodeExtern{
  403. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  404. {Key: "/1234/attributes", Value: stringp("garbage")},
  405. }},
  406. {Key: "/1234", Nodes: []*store.NodeExtern{
  407. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  408. {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  409. {Key: "/1234/strange"},
  410. }},
  411. }
  412. for i, tt := range tests {
  413. if _, err := nodeToMember(tt); err == nil {
  414. t.Errorf("#%d: unexpected nil error", i)
  415. }
  416. }
  417. }
  418. func TestClusterAddMember(t *testing.T) {
  419. st := &storeRecorder{}
  420. c := newTestCluster(nil)
  421. c.SetStore(st)
  422. c.AddMember(newTestMember(1, nil, "node1", nil))
  423. wactions := []testutil.Action{
  424. {
  425. Name: "Create",
  426. Params: []interface{}{
  427. path.Join(storeMembersPrefix, "1", "raftAttributes"),
  428. false,
  429. `{"peerURLs":null}`,
  430. false,
  431. store.Permanent,
  432. },
  433. },
  434. }
  435. if g := st.Action(); !reflect.DeepEqual(g, wactions) {
  436. t.Errorf("actions = %v, want %v", g, wactions)
  437. }
  438. }
  439. func TestClusterMembers(t *testing.T) {
  440. cls := &cluster{
  441. members: map[types.ID]*Member{
  442. 1: {ID: 1},
  443. 20: {ID: 20},
  444. 100: {ID: 100},
  445. 5: {ID: 5},
  446. 50: {ID: 50},
  447. },
  448. }
  449. w := []*Member{
  450. {ID: 1},
  451. {ID: 5},
  452. {ID: 20},
  453. {ID: 50},
  454. {ID: 100},
  455. }
  456. if g := cls.Members(); !reflect.DeepEqual(g, w) {
  457. t.Fatalf("Members()=%#v, want %#v", g, w)
  458. }
  459. }
  460. func TestClusterRemoveMember(t *testing.T) {
  461. st := &storeRecorder{}
  462. c := newTestCluster(nil)
  463. c.SetStore(st)
  464. c.RemoveMember(1)
  465. wactions := []testutil.Action{
  466. {Name: "Delete", Params: []interface{}{memberStoreKey(1), true, true}},
  467. {Name: "Create", Params: []interface{}{removedMemberStoreKey(1), false, "", false, store.Permanent}},
  468. }
  469. if !reflect.DeepEqual(st.Action(), wactions) {
  470. t.Errorf("actions = %v, want %v", st.Action(), wactions)
  471. }
  472. }
  473. func TestClusterUpdateAttributes(t *testing.T) {
  474. name := "etcd"
  475. clientURLs := []string{"http://127.0.0.1:4001"}
  476. tests := []struct {
  477. mems []*Member
  478. removed map[types.ID]bool
  479. wmems []*Member
  480. }{
  481. // update attributes of existing member
  482. {
  483. []*Member{
  484. newTestMember(1, nil, "", nil),
  485. },
  486. nil,
  487. []*Member{
  488. newTestMember(1, nil, name, clientURLs),
  489. },
  490. },
  491. // update attributes of removed member
  492. {
  493. nil,
  494. map[types.ID]bool{types.ID(1): true},
  495. nil,
  496. },
  497. }
  498. for i, tt := range tests {
  499. c := newTestCluster(tt.mems)
  500. c.removed = tt.removed
  501. c.UpdateAttributes(types.ID(1), Attributes{Name: name, ClientURLs: clientURLs})
  502. if g := c.Members(); !reflect.DeepEqual(g, tt.wmems) {
  503. t.Errorf("#%d: members = %+v, want %+v", i, g, tt.wmems)
  504. }
  505. }
  506. }
  507. func TestNodeToMember(t *testing.T) {
  508. n := &store.NodeExtern{Key: "/1234", Nodes: []*store.NodeExtern{
  509. {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  510. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  511. }}
  512. wm := &Member{ID: 0x1234, RaftAttributes: RaftAttributes{}, Attributes: Attributes{Name: "node1"}}
  513. m, err := nodeToMember(n)
  514. if err != nil {
  515. t.Fatalf("unexpected nodeToMember error: %v", err)
  516. }
  517. if !reflect.DeepEqual(m, wm) {
  518. t.Errorf("member = %+v, want %+v", m, wm)
  519. }
  520. }
  521. func newTestCluster(membs []*Member) *cluster {
  522. c := &cluster{members: make(map[types.ID]*Member), removed: make(map[types.ID]bool)}
  523. for _, m := range membs {
  524. c.members[m.ID] = m
  525. }
  526. return c
  527. }
  528. func stringp(s string) *string { return &s }