mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
client: Return the server's cluster ID as part of the Response
This allows the client to spot if the cluster ID changes, which would indicate that the cluster has been rebuilt and watches may be out of sync. Helps work around #6652.
This commit is contained in:
parent
92c987f75d
commit
43df091067
@ -272,6 +272,10 @@ type Response struct {
|
|||||||
// Index holds the cluster-level index at the time the Response was generated.
|
// Index holds the cluster-level index at the time the Response was generated.
|
||||||
// This index is not tied to the Node(s) contained in this Response.
|
// This index is not tied to the Node(s) contained in this Response.
|
||||||
Index uint64 `json:"-"`
|
Index uint64 `json:"-"`
|
||||||
|
|
||||||
|
// ClusterID holds the cluster-level ID reported by the server. This
|
||||||
|
// should be different for different etcd clusters.
|
||||||
|
ClusterID string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
@ -665,6 +669,7 @@ func unmarshalSuccessfulKeysResponse(header http.Header, body []byte) (*Response
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
res.ClusterID = header.Get("X-Etcd-Cluster-ID")
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,14 +673,15 @@ func TestUnmarshalSuccessfulResponse(t *testing.T) {
|
|||||||
expiration.UnmarshalText([]byte("2015-04-07T04:40:23.044979686Z"))
|
expiration.UnmarshalText([]byte("2015-04-07T04:40:23.044979686Z"))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
hdr string
|
indexHdr string
|
||||||
|
clusterIDHdr string
|
||||||
body string
|
body string
|
||||||
wantRes *Response
|
wantRes *Response
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
// Neither PrevNode or Node
|
// Neither PrevNode or Node
|
||||||
{
|
{
|
||||||
hdr: "1",
|
indexHdr: "1",
|
||||||
body: `{"action":"delete"}`,
|
body: `{"action":"delete"}`,
|
||||||
wantRes: &Response{Action: "delete", Index: 1},
|
wantRes: &Response{Action: "delete", Index: 1},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
@ -688,7 +689,7 @@ func TestUnmarshalSuccessfulResponse(t *testing.T) {
|
|||||||
|
|
||||||
// PrevNode
|
// PrevNode
|
||||||
{
|
{
|
||||||
hdr: "15",
|
indexHdr: "15",
|
||||||
body: `{"action":"delete", "prevNode": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
|
body: `{"action":"delete", "prevNode": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
|
||||||
wantRes: &Response{
|
wantRes: &Response{
|
||||||
Action: "delete",
|
Action: "delete",
|
||||||
@ -706,7 +707,7 @@ func TestUnmarshalSuccessfulResponse(t *testing.T) {
|
|||||||
|
|
||||||
// Node
|
// Node
|
||||||
{
|
{
|
||||||
hdr: "15",
|
indexHdr: "15",
|
||||||
body: `{"action":"get", "node": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10, "ttl": 10, "expiration": "2015-04-07T04:40:23.044979686Z"}}`,
|
body: `{"action":"get", "node": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10, "ttl": 10, "expiration": "2015-04-07T04:40:23.044979686Z"}}`,
|
||||||
wantRes: &Response{
|
wantRes: &Response{
|
||||||
Action: "get",
|
Action: "get",
|
||||||
@ -726,7 +727,8 @@ func TestUnmarshalSuccessfulResponse(t *testing.T) {
|
|||||||
|
|
||||||
// Node Dir
|
// Node Dir
|
||||||
{
|
{
|
||||||
hdr: "15",
|
indexHdr: "15",
|
||||||
|
clusterIDHdr: "abcdef",
|
||||||
body: `{"action":"get", "node": {"key": "/foo", "dir": true, "modifiedIndex": 12, "createdIndex": 10}}`,
|
body: `{"action":"get", "node": {"key": "/foo", "dir": true, "modifiedIndex": 12, "createdIndex": 10}}`,
|
||||||
wantRes: &Response{
|
wantRes: &Response{
|
||||||
Action: "get",
|
Action: "get",
|
||||||
@ -738,13 +740,14 @@ func TestUnmarshalSuccessfulResponse(t *testing.T) {
|
|||||||
CreatedIndex: 10,
|
CreatedIndex: 10,
|
||||||
},
|
},
|
||||||
PrevNode: nil,
|
PrevNode: nil,
|
||||||
|
ClusterID: "abcdef",
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
// PrevNode and Node
|
// PrevNode and Node
|
||||||
{
|
{
|
||||||
hdr: "15",
|
indexHdr: "15",
|
||||||
body: `{"action":"update", "prevNode": {"key": "/foo", "value": "baz", "modifiedIndex": 10, "createdIndex": 10}, "node": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
|
body: `{"action":"update", "prevNode": {"key": "/foo", "value": "baz", "modifiedIndex": 10, "createdIndex": 10}, "node": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
|
||||||
wantRes: &Response{
|
wantRes: &Response{
|
||||||
Action: "update",
|
Action: "update",
|
||||||
@ -767,7 +770,7 @@ func TestUnmarshalSuccessfulResponse(t *testing.T) {
|
|||||||
|
|
||||||
// Garbage in body
|
// Garbage in body
|
||||||
{
|
{
|
||||||
hdr: "",
|
indexHdr: "",
|
||||||
body: `garbage`,
|
body: `garbage`,
|
||||||
wantRes: nil,
|
wantRes: nil,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
@ -775,7 +778,7 @@ func TestUnmarshalSuccessfulResponse(t *testing.T) {
|
|||||||
|
|
||||||
// non-integer index
|
// non-integer index
|
||||||
{
|
{
|
||||||
hdr: "poo",
|
indexHdr: "poo",
|
||||||
body: `{}`,
|
body: `{}`,
|
||||||
wantRes: nil,
|
wantRes: nil,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
@ -784,7 +787,7 @@ func TestUnmarshalSuccessfulResponse(t *testing.T) {
|
|||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
h := make(http.Header)
|
h := make(http.Header)
|
||||||
h.Add("X-Etcd-Index", tt.hdr)
|
h.Add("X-Etcd-Index", tt.indexHdr)
|
||||||
res, err := unmarshalSuccessfulKeysResponse(h, []byte(tt.body))
|
res, err := unmarshalSuccessfulKeysResponse(h, []byte(tt.body))
|
||||||
if tt.wantErr != (err != nil) {
|
if tt.wantErr != (err != nil) {
|
||||||
t.Errorf("#%d: wantErr=%t, err=%v", i, tt.wantErr, err)
|
t.Errorf("#%d: wantErr=%t, err=%v", i, tt.wantErr, err)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user