Merge pull request #1620 from yichengq/204

etcdserver: not record attributes when add member
This commit is contained in:
Yicheng Qin 2014-11-12 21:33:53 -08:00
commit 1e1535e6f9
4 changed files with 73 additions and 80 deletions

View File

@ -327,7 +327,8 @@ func (c *Cluster) ValidateConfigurationChange(cc raftpb.ConfChange) error {
return nil return nil
} }
// AddMember puts a new Member into the store. // AddMember adds a new Member into the cluster, and saves the given member's
// raftAttributes into the store. The given member should have empty attributes.
// A Member with a matching id must not exist. // A Member with a matching id must not exist.
func (c *Cluster) AddMember(m *Member) { func (c *Cluster) AddMember(m *Member) {
c.Lock() c.Lock()
@ -340,14 +341,6 @@ func (c *Cluster) AddMember(m *Member) {
if _, err := c.store.Create(p, false, string(b), false, store.Permanent); err != nil { if _, err := c.store.Create(p, false, string(b), false, store.Permanent); err != nil {
log.Panicf("create raftAttributes should never fail: %v", err) log.Panicf("create raftAttributes should never fail: %v", err)
} }
b, err = json.Marshal(m.Attributes)
if err != nil {
log.Panicf("marshal attributes should never fail: %v", err)
}
p = path.Join(memberStoreKey(m.ID), attributesSuffix)
if _, err := c.store.Create(p, false, string(b), false, store.Permanent); err != nil {
log.Panicf("create attributes should never fail: %v", err)
}
c.members[m.ID] = m c.members[m.ID] = m
} }
@ -390,20 +383,26 @@ func (c *Cluster) UpdateMember(nm *Member) {
// the child nodes of the given node should be sorted by key. // the child nodes of the given node should be sorted by key.
func nodeToMember(n *store.NodeExtern) (*Member, error) { func nodeToMember(n *store.NodeExtern) (*Member, error) {
m := &Member{ID: mustParseMemberIDFromKey(n.Key)} m := &Member{ID: mustParseMemberIDFromKey(n.Key)}
if len(n.Nodes) != 2 { attrs := make(map[string][]byte)
return m, fmt.Errorf("len(nodes) = %d, want 2", len(n.Nodes)) raftAttrKey := path.Join(n.Key, raftAttributesSuffix)
attrKey := path.Join(n.Key, attributesSuffix)
for _, nn := range n.Nodes {
if nn.Key != raftAttrKey && nn.Key != attrKey {
return nil, fmt.Errorf("unknown key %q", nn.Key)
}
attrs[nn.Key] = []byte(*nn.Value)
} }
if w := path.Join(n.Key, attributesSuffix); n.Nodes[0].Key != w { if data := attrs[raftAttrKey]; data != nil {
return m, fmt.Errorf("key = %v, want %v", n.Nodes[0].Key, w) if err := json.Unmarshal(data, &m.RaftAttributes); err != nil {
return nil, fmt.Errorf("unmarshal raftAttributes error: %v", err)
}
} else {
return nil, fmt.Errorf("raftAttributes key doesn't exist")
} }
if err := json.Unmarshal([]byte(*n.Nodes[0].Value), &m.Attributes); err != nil { if data := attrs[attrKey]; data != nil {
return m, fmt.Errorf("unmarshal attributes error: %v", err) if err := json.Unmarshal(data, &m.Attributes); err != nil {
} return m, fmt.Errorf("unmarshal attributes error: %v", err)
if w := path.Join(n.Key, raftAttributesSuffix); n.Nodes[1].Key != w { }
return m, fmt.Errorf("key = %v, want %v", n.Nodes[1].Key, w)
}
if err := json.Unmarshal([]byte(*n.Nodes[1].Value), &m.RaftAttributes); err != nil {
return m, fmt.Errorf("unmarshal raftAttributes error: %v", err)
} }
return m, nil return m, nil
} }

View File

@ -82,20 +82,21 @@ func TestClusterFromStore(t *testing.T) {
mems []*Member mems []*Member
}{ }{
{ {
[]*Member{newTestMember(1, nil, "node1", nil)}, []*Member{newTestMember(1, nil, "", nil)},
}, },
{ {
nil, nil,
}, },
{ {
[]*Member{ []*Member{
newTestMember(1, nil, "node1", nil), newTestMember(1, nil, "", nil),
newTestMember(2, nil, "node2", nil), newTestMember(2, nil, "", nil),
}, },
}, },
} }
for i, tt := range tests { for i, tt := range tests {
hc := newTestCluster(nil) hc := newTestCluster(nil)
hc.SetStore(store.New())
for _, m := range tt.mems { for _, m := range tt.mems {
hc.AddMember(m) hc.AddMember(m)
} }
@ -500,22 +501,22 @@ func TestNodeToMemberBad(t *testing.T) {
{Key: "/1234/strange"}, {Key: "/1234/strange"},
}}, }},
{Key: "/1234", Nodes: []*store.NodeExtern{ {Key: "/1234", Nodes: []*store.NodeExtern{
{Key: "/1234/dynamic", Value: stringp("garbage")}, {Key: "/1234/raftAttributes", Value: stringp("garbage")},
}}, }},
{Key: "/1234", Nodes: []*store.NodeExtern{ {Key: "/1234", Nodes: []*store.NodeExtern{
{Key: "/1234/dynamic", Value: stringp(`{"peerURLs":null}`)}, {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
}}, }},
{Key: "/1234", Nodes: []*store.NodeExtern{ {Key: "/1234", Nodes: []*store.NodeExtern{
{Key: "/1234/dynamic", Value: stringp(`{"peerURLs":null}`)}, {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
{Key: "/1234/strange"}, {Key: "/1234/strange"},
}}, }},
{Key: "/1234", Nodes: []*store.NodeExtern{ {Key: "/1234", Nodes: []*store.NodeExtern{
{Key: "/1234/dynamic", Value: stringp(`{"peerURLs":null}`)}, {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
{Key: "/1234/static", Value: stringp("garbage")}, {Key: "/1234/attributes", Value: stringp("garbage")},
}}, }},
{Key: "/1234", Nodes: []*store.NodeExtern{ {Key: "/1234", Nodes: []*store.NodeExtern{
{Key: "/1234/dynamic", Value: stringp(`{"peerURLs":null}`)}, {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
{Key: "/1234/static", Value: stringp(`{"name":"node1","clientURLs":null}`)}, {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
{Key: "/1234/strange"}, {Key: "/1234/strange"},
}}, }},
} }
@ -543,16 +544,6 @@ func TestClusterAddMember(t *testing.T) {
store.Permanent, store.Permanent,
}, },
}, },
{
name: "Create",
params: []interface{}{
path.Join(storeMembersPrefix, "1", "attributes"),
false,
`{"name":"node1"}`,
false,
store.Permanent,
},
},
} }
if g := st.Action(); !reflect.DeepEqual(g, wactions) { if g := st.Action(); !reflect.DeepEqual(g, wactions) {
t.Errorf("actions = %v, want %v", g, wactions) t.Errorf("actions = %v, want %v", g, wactions)
@ -656,9 +647,8 @@ func TestNodeToMember(t *testing.T) {
func newTestCluster(membs []*Member) *Cluster { func newTestCluster(membs []*Member) *Cluster {
c := &Cluster{members: make(map[types.ID]*Member), removed: make(map[types.ID]bool)} c := &Cluster{members: make(map[types.ID]*Member), removed: make(map[types.ID]bool)}
c.store = store.New() for _, m := range membs {
for i := range membs { c.members[m.ID] = m
c.AddMember(membs[i])
} }
return c return c
} }

View File

@ -554,8 +554,8 @@ func testServer(t *testing.T, ns uint64) {
g, w := resp.Event.Node, &store.NodeExtern{ g, w := resp.Event.Node, &store.NodeExtern{
Key: "/foo", Key: "/foo",
ModifiedIndex: uint64(i) + 2*ns, ModifiedIndex: uint64(i) + ns,
CreatedIndex: uint64(i) + 2*ns, CreatedIndex: uint64(i) + ns,
Value: stringp("bar"), Value: stringp("bar"),
} }
@ -993,7 +993,9 @@ func TestRemoveMember(t *testing.T) {
Nodes: []uint64{1234, 2345, 3456}, Nodes: []uint64{1234, 2345, 3456},
}, },
} }
cl := newTestCluster([]*Member{{ID: 1234}}) cl := newTestCluster(nil)
cl.SetStore(store.New())
cl.AddMember(&Member{ID: 1234})
s := &EtcdServer{ s := &EtcdServer{
node: n, node: n,
store: &storeRecorder{}, store: &storeRecorder{},
@ -1027,7 +1029,9 @@ func TestUpdateMember(t *testing.T) {
Nodes: []uint64{1234, 2345, 3456}, Nodes: []uint64{1234, 2345, 3456},
}, },
} }
cl := newTestCluster([]*Member{{ID: 1234}}) cl := newTestCluster(nil)
cl.SetStore(store.New())
cl.AddMember(&Member{ID: 1234})
s := &EtcdServer{ s := &EtcdServer{
node: n, node: n,
store: &storeRecorder{}, store: &storeRecorder{},

View File

@ -55,19 +55,19 @@ func TestV2Set(t *testing.T) {
"/v2/keys/foo/bar", "/v2/keys/foo/bar",
v, v,
http.StatusCreated, http.StatusCreated,
`{"action":"set","node":{"key":"/foo/bar","value":"bar","modifiedIndex":4,"createdIndex":4}}`, `{"action":"set","node":{"key":"/foo/bar","value":"bar","modifiedIndex":3,"createdIndex":3}}`,
}, },
{ {
"/v2/keys/foodir?dir=true", "/v2/keys/foodir?dir=true",
url.Values{}, url.Values{},
http.StatusCreated, http.StatusCreated,
`{"action":"set","node":{"key":"/foodir","dir":true,"modifiedIndex":5,"createdIndex":5}}`, `{"action":"set","node":{"key":"/foodir","dir":true,"modifiedIndex":4,"createdIndex":4}}`,
}, },
{ {
"/v2/keys/fooempty", "/v2/keys/fooempty",
url.Values(map[string][]string{"value": {""}}), url.Values(map[string][]string{"value": {""}}),
http.StatusCreated, http.StatusCreated,
`{"action":"set","node":{"key":"/fooempty","value":"","modifiedIndex":6,"createdIndex":6}}`, `{"action":"set","node":{"key":"/fooempty","value":"","modifiedIndex":5,"createdIndex":5}}`,
}, },
} }
@ -216,12 +216,12 @@ func TestV2CAS(t *testing.T) {
}, },
{ {
"/v2/keys/cas/foo", "/v2/keys/cas/foo",
url.Values(map[string][]string{"value": {"YYY"}, "prevIndex": {"4"}}), url.Values(map[string][]string{"value": {"YYY"}, "prevIndex": {"3"}}),
http.StatusOK, http.StatusOK,
map[string]interface{}{ map[string]interface{}{
"node": map[string]interface{}{ "node": map[string]interface{}{
"value": "YYY", "value": "YYY",
"modifiedIndex": float64(5), "modifiedIndex": float64(4),
}, },
"action": "compareAndSwap", "action": "compareAndSwap",
}, },
@ -233,8 +233,8 @@ func TestV2CAS(t *testing.T) {
map[string]interface{}{ map[string]interface{}{
"errorCode": float64(101), "errorCode": float64(101),
"message": "Compare failed", "message": "Compare failed",
"cause": "[10 != 5]", "cause": "[10 != 4]",
"index": float64(5), "index": float64(4),
}, },
}, },
{ {
@ -283,7 +283,7 @@ func TestV2CAS(t *testing.T) {
map[string]interface{}{ map[string]interface{}{
"errorCode": float64(101), "errorCode": float64(101),
"message": "Compare failed", "message": "Compare failed",
"cause": "[bad_value != ZZZ] [100 != 6]", "cause": "[bad_value != ZZZ] [100 != 5]",
}, },
}, },
{ {
@ -293,12 +293,12 @@ func TestV2CAS(t *testing.T) {
map[string]interface{}{ map[string]interface{}{
"errorCode": float64(101), "errorCode": float64(101),
"message": "Compare failed", "message": "Compare failed",
"cause": "[100 != 6]", "cause": "[100 != 5]",
}, },
}, },
{ {
"/v2/keys/cas/foo", "/v2/keys/cas/foo",
url.Values(map[string][]string{"value": {"XXX"}, "prevValue": {"bad_value"}, "prevIndex": {"6"}}), url.Values(map[string][]string{"value": {"XXX"}, "prevValue": {"bad_value"}, "prevIndex": {"5"}}),
http.StatusPreconditionFailed, http.StatusPreconditionFailed,
map[string]interface{}{ map[string]interface{}{
"errorCode": float64(101), "errorCode": float64(101),
@ -448,7 +448,7 @@ func TestV2CAD(t *testing.T) {
map[string]interface{}{ map[string]interface{}{
"errorCode": float64(101), "errorCode": float64(101),
"message": "Compare failed", "message": "Compare failed",
"cause": "[100 != 4]", "cause": "[100 != 3]",
}, },
}, },
{ {
@ -460,12 +460,12 @@ func TestV2CAD(t *testing.T) {
}, },
}, },
{ {
"/v2/keys/foo?prevIndex=4", "/v2/keys/foo?prevIndex=3",
http.StatusOK, http.StatusOK,
map[string]interface{}{ map[string]interface{}{
"node": map[string]interface{}{ "node": map[string]interface{}{
"key": "/foo", "key": "/foo",
"modifiedIndex": float64(6), "modifiedIndex": float64(5),
}, },
"action": "compareAndDelete", "action": "compareAndDelete",
}, },
@ -493,7 +493,7 @@ func TestV2CAD(t *testing.T) {
map[string]interface{}{ map[string]interface{}{
"node": map[string]interface{}{ "node": map[string]interface{}{
"key": "/foovalue", "key": "/foovalue",
"modifiedIndex": float64(7), "modifiedIndex": float64(6),
}, },
"action": "compareAndDelete", "action": "compareAndDelete",
}, },
@ -531,7 +531,7 @@ func TestV2Unique(t *testing.T) {
http.StatusCreated, http.StatusCreated,
map[string]interface{}{ map[string]interface{}{
"node": map[string]interface{}{ "node": map[string]interface{}{
"key": "/foo/4", "key": "/foo/3",
"value": "XXX", "value": "XXX",
}, },
"action": "create", "action": "create",
@ -543,7 +543,7 @@ func TestV2Unique(t *testing.T) {
http.StatusCreated, http.StatusCreated,
map[string]interface{}{ map[string]interface{}{
"node": map[string]interface{}{ "node": map[string]interface{}{
"key": "/foo/5", "key": "/foo/4",
"value": "XXX", "value": "XXX",
}, },
"action": "create", "action": "create",
@ -555,7 +555,7 @@ func TestV2Unique(t *testing.T) {
http.StatusCreated, http.StatusCreated,
map[string]interface{}{ map[string]interface{}{
"node": map[string]interface{}{ "node": map[string]interface{}{
"key": "/bar/6", "key": "/bar/5",
"value": "XXX", "value": "XXX",
}, },
"action": "create", "action": "create",
@ -617,8 +617,8 @@ func TestV2Get(t *testing.T) {
map[string]interface{}{ map[string]interface{}{
"key": "/foo/bar", "key": "/foo/bar",
"dir": true, "dir": true,
"createdIndex": float64(4), "createdIndex": float64(3),
"modifiedIndex": float64(4), "modifiedIndex": float64(3),
}, },
}, },
}, },
@ -636,14 +636,14 @@ func TestV2Get(t *testing.T) {
map[string]interface{}{ map[string]interface{}{
"key": "/foo/bar", "key": "/foo/bar",
"dir": true, "dir": true,
"createdIndex": float64(4), "createdIndex": float64(3),
"modifiedIndex": float64(4), "modifiedIndex": float64(3),
"nodes": []interface{}{ "nodes": []interface{}{
map[string]interface{}{ map[string]interface{}{
"key": "/foo/bar/zar", "key": "/foo/bar/zar",
"value": "XXX", "value": "XXX",
"createdIndex": float64(4), "createdIndex": float64(3),
"modifiedIndex": float64(4), "modifiedIndex": float64(3),
}, },
}, },
}, },
@ -711,8 +711,8 @@ func TestV2QuorumGet(t *testing.T) {
map[string]interface{}{ map[string]interface{}{
"key": "/foo/bar", "key": "/foo/bar",
"dir": true, "dir": true,
"createdIndex": float64(4), "createdIndex": float64(3),
"modifiedIndex": float64(4), "modifiedIndex": float64(3),
}, },
}, },
}, },
@ -730,14 +730,14 @@ func TestV2QuorumGet(t *testing.T) {
map[string]interface{}{ map[string]interface{}{
"key": "/foo/bar", "key": "/foo/bar",
"dir": true, "dir": true,
"createdIndex": float64(4), "createdIndex": float64(3),
"modifiedIndex": float64(4), "modifiedIndex": float64(3),
"nodes": []interface{}{ "nodes": []interface{}{
map[string]interface{}{ map[string]interface{}{
"key": "/foo/bar/zar", "key": "/foo/bar/zar",
"value": "XXX", "value": "XXX",
"createdIndex": float64(4), "createdIndex": float64(3),
"modifiedIndex": float64(4), "modifiedIndex": float64(3),
}, },
}, },
}, },
@ -797,7 +797,7 @@ func TestV2Watch(t *testing.T) {
"node": map[string]interface{}{ "node": map[string]interface{}{
"key": "/foo/bar", "key": "/foo/bar",
"value": "XXX", "value": "XXX",
"modifiedIndex": float64(4), "modifiedIndex": float64(3),
}, },
"action": "set", "action": "set",
} }
@ -818,7 +818,7 @@ func TestV2WatchWithIndex(t *testing.T) {
var body map[string]interface{} var body map[string]interface{}
c := make(chan bool, 1) c := make(chan bool, 1)
go func() { go func() {
resp, _ := tc.Get(fmt.Sprintf("%s%s", u, "/v2/keys/foo/bar?wait=true&waitIndex=5")) resp, _ := tc.Get(fmt.Sprintf("%s%s", u, "/v2/keys/foo/bar?wait=true&waitIndex=4"))
body = tc.ReadBodyJSON(resp) body = tc.ReadBodyJSON(resp)
c <- true c <- true
}() }()
@ -855,7 +855,7 @@ func TestV2WatchWithIndex(t *testing.T) {
"node": map[string]interface{}{ "node": map[string]interface{}{
"key": "/foo/bar", "key": "/foo/bar",
"value": "XXX", "value": "XXX",
"modifiedIndex": float64(5), "modifiedIndex": float64(4),
}, },
"action": "set", "action": "set",
} }