*: add cluster version and cluster version detection.
Cluster version is the min major.minor of all members in
the etcd cluster. Cluster version is set to the min version
that a etcd member is compatible with when first bootstrapp.
During a rolling upgrades, the cluster version will be updated
automatically.
For example:
```
Cluster [a:1, b:1 ,c:1] -> clusterVersion 1
update a -> 2, b -> 2
after a detection
Cluster [a:2, b:2 ,c:1] -> clusterVersion 1, since c is still 1
update c -> 2
after a detection
Cluster [a:2, b:2 ,c:2] -> clusterVersion 2
```
The API/raft component can utilize clusterVersion to determine if
it can accept a client request or a raft RPC.
We choose polling rather than pushing since we want to use the same
logic for cluster version detection and (TODO) cluster version checking.
Before a member actually joins a etcd cluster, it should check the version
of the cluster. Push does not work since the other members cannot push
version info to it before it actually joins. Moreover, we do not want our
raft RPC system (which is doing the heartbeat pushing) to coordinate cluster version.
2015-04-29 20:56:34 +03:00
|
|
|
// Copyright 2015 CoreOS, Inc.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package etcdserver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
|
2015-05-14 17:57:25 +03:00
|
|
|
"github.com/coreos/etcd/pkg/types"
|
2015-05-14 03:04:46 +03:00
|
|
|
"github.com/coreos/etcd/version"
|
*: add cluster version and cluster version detection.
Cluster version is the min major.minor of all members in
the etcd cluster. Cluster version is set to the min version
that a etcd member is compatible with when first bootstrapp.
During a rolling upgrades, the cluster version will be updated
automatically.
For example:
```
Cluster [a:1, b:1 ,c:1] -> clusterVersion 1
update a -> 2, b -> 2
after a detection
Cluster [a:2, b:2 ,c:1] -> clusterVersion 1, since c is still 1
update c -> 2
after a detection
Cluster [a:2, b:2 ,c:2] -> clusterVersion 2
```
The API/raft component can utilize clusterVersion to determine if
it can accept a client request or a raft RPC.
We choose polling rather than pushing since we want to use the same
logic for cluster version detection and (TODO) cluster version checking.
Before a member actually joins a etcd cluster, it should check the version
of the cluster. Push does not work since the other members cannot push
version info to it before it actually joins. Moreover, we do not want our
raft RPC system (which is doing the heartbeat pushing) to coordinate cluster version.
2015-04-29 20:56:34 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestDecideClusterVersion(t *testing.T) {
|
|
|
|
tests := []struct {
|
2015-05-14 03:04:46 +03:00
|
|
|
vers map[string]*version.Versions
|
*: add cluster version and cluster version detection.
Cluster version is the min major.minor of all members in
the etcd cluster. Cluster version is set to the min version
that a etcd member is compatible with when first bootstrapp.
During a rolling upgrades, the cluster version will be updated
automatically.
For example:
```
Cluster [a:1, b:1 ,c:1] -> clusterVersion 1
update a -> 2, b -> 2
after a detection
Cluster [a:2, b:2 ,c:1] -> clusterVersion 1, since c is still 1
update c -> 2
after a detection
Cluster [a:2, b:2 ,c:2] -> clusterVersion 2
```
The API/raft component can utilize clusterVersion to determine if
it can accept a client request or a raft RPC.
We choose polling rather than pushing since we want to use the same
logic for cluster version detection and (TODO) cluster version checking.
Before a member actually joins a etcd cluster, it should check the version
of the cluster. Push does not work since the other members cannot push
version info to it before it actually joins. Moreover, we do not want our
raft RPC system (which is doing the heartbeat pushing) to coordinate cluster version.
2015-04-29 20:56:34 +03:00
|
|
|
wdver *semver.Version
|
|
|
|
}{
|
|
|
|
{
|
2015-08-22 04:52:16 +03:00
|
|
|
map[string]*version.Versions{"a": {Server: "2.0.0"}},
|
*: add cluster version and cluster version detection.
Cluster version is the min major.minor of all members in
the etcd cluster. Cluster version is set to the min version
that a etcd member is compatible with when first bootstrapp.
During a rolling upgrades, the cluster version will be updated
automatically.
For example:
```
Cluster [a:1, b:1 ,c:1] -> clusterVersion 1
update a -> 2, b -> 2
after a detection
Cluster [a:2, b:2 ,c:1] -> clusterVersion 1, since c is still 1
update c -> 2
after a detection
Cluster [a:2, b:2 ,c:2] -> clusterVersion 2
```
The API/raft component can utilize clusterVersion to determine if
it can accept a client request or a raft RPC.
We choose polling rather than pushing since we want to use the same
logic for cluster version detection and (TODO) cluster version checking.
Before a member actually joins a etcd cluster, it should check the version
of the cluster. Push does not work since the other members cannot push
version info to it before it actually joins. Moreover, we do not want our
raft RPC system (which is doing the heartbeat pushing) to coordinate cluster version.
2015-04-29 20:56:34 +03:00
|
|
|
semver.Must(semver.NewVersion("2.0.0")),
|
|
|
|
},
|
|
|
|
// unknow
|
|
|
|
{
|
2015-05-14 03:04:46 +03:00
|
|
|
map[string]*version.Versions{"a": nil},
|
*: add cluster version and cluster version detection.
Cluster version is the min major.minor of all members in
the etcd cluster. Cluster version is set to the min version
that a etcd member is compatible with when first bootstrapp.
During a rolling upgrades, the cluster version will be updated
automatically.
For example:
```
Cluster [a:1, b:1 ,c:1] -> clusterVersion 1
update a -> 2, b -> 2
after a detection
Cluster [a:2, b:2 ,c:1] -> clusterVersion 1, since c is still 1
update c -> 2
after a detection
Cluster [a:2, b:2 ,c:2] -> clusterVersion 2
```
The API/raft component can utilize clusterVersion to determine if
it can accept a client request or a raft RPC.
We choose polling rather than pushing since we want to use the same
logic for cluster version detection and (TODO) cluster version checking.
Before a member actually joins a etcd cluster, it should check the version
of the cluster. Push does not work since the other members cannot push
version info to it before it actually joins. Moreover, we do not want our
raft RPC system (which is doing the heartbeat pushing) to coordinate cluster version.
2015-04-29 20:56:34 +03:00
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
2015-08-22 04:52:16 +03:00
|
|
|
map[string]*version.Versions{"a": {Server: "2.0.0"}, "b": {Server: "2.1.0"}, "c": {Server: "2.1.0"}},
|
*: add cluster version and cluster version detection.
Cluster version is the min major.minor of all members in
the etcd cluster. Cluster version is set to the min version
that a etcd member is compatible with when first bootstrapp.
During a rolling upgrades, the cluster version will be updated
automatically.
For example:
```
Cluster [a:1, b:1 ,c:1] -> clusterVersion 1
update a -> 2, b -> 2
after a detection
Cluster [a:2, b:2 ,c:1] -> clusterVersion 1, since c is still 1
update c -> 2
after a detection
Cluster [a:2, b:2 ,c:2] -> clusterVersion 2
```
The API/raft component can utilize clusterVersion to determine if
it can accept a client request or a raft RPC.
We choose polling rather than pushing since we want to use the same
logic for cluster version detection and (TODO) cluster version checking.
Before a member actually joins a etcd cluster, it should check the version
of the cluster. Push does not work since the other members cannot push
version info to it before it actually joins. Moreover, we do not want our
raft RPC system (which is doing the heartbeat pushing) to coordinate cluster version.
2015-04-29 20:56:34 +03:00
|
|
|
semver.Must(semver.NewVersion("2.0.0")),
|
|
|
|
},
|
|
|
|
{
|
2015-08-22 04:52:16 +03:00
|
|
|
map[string]*version.Versions{"a": {Server: "2.1.0"}, "b": {Server: "2.1.0"}, "c": {Server: "2.1.0"}},
|
*: add cluster version and cluster version detection.
Cluster version is the min major.minor of all members in
the etcd cluster. Cluster version is set to the min version
that a etcd member is compatible with when first bootstrapp.
During a rolling upgrades, the cluster version will be updated
automatically.
For example:
```
Cluster [a:1, b:1 ,c:1] -> clusterVersion 1
update a -> 2, b -> 2
after a detection
Cluster [a:2, b:2 ,c:1] -> clusterVersion 1, since c is still 1
update c -> 2
after a detection
Cluster [a:2, b:2 ,c:2] -> clusterVersion 2
```
The API/raft component can utilize clusterVersion to determine if
it can accept a client request or a raft RPC.
We choose polling rather than pushing since we want to use the same
logic for cluster version detection and (TODO) cluster version checking.
Before a member actually joins a etcd cluster, it should check the version
of the cluster. Push does not work since the other members cannot push
version info to it before it actually joins. Moreover, we do not want our
raft RPC system (which is doing the heartbeat pushing) to coordinate cluster version.
2015-04-29 20:56:34 +03:00
|
|
|
semver.Must(semver.NewVersion("2.1.0")),
|
|
|
|
},
|
|
|
|
{
|
2015-08-22 04:52:16 +03:00
|
|
|
map[string]*version.Versions{"a": nil, "b": {Server: "2.1.0"}, "c": {Server: "2.1.0"}},
|
*: add cluster version and cluster version detection.
Cluster version is the min major.minor of all members in
the etcd cluster. Cluster version is set to the min version
that a etcd member is compatible with when first bootstrapp.
During a rolling upgrades, the cluster version will be updated
automatically.
For example:
```
Cluster [a:1, b:1 ,c:1] -> clusterVersion 1
update a -> 2, b -> 2
after a detection
Cluster [a:2, b:2 ,c:1] -> clusterVersion 1, since c is still 1
update c -> 2
after a detection
Cluster [a:2, b:2 ,c:2] -> clusterVersion 2
```
The API/raft component can utilize clusterVersion to determine if
it can accept a client request or a raft RPC.
We choose polling rather than pushing since we want to use the same
logic for cluster version detection and (TODO) cluster version checking.
Before a member actually joins a etcd cluster, it should check the version
of the cluster. Push does not work since the other members cannot push
version info to it before it actually joins. Moreover, we do not want our
raft RPC system (which is doing the heartbeat pushing) to coordinate cluster version.
2015-04-29 20:56:34 +03:00
|
|
|
nil,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tt := range tests {
|
|
|
|
dver := decideClusterVersion(tt.vers)
|
|
|
|
if !reflect.DeepEqual(dver, tt.wdver) {
|
|
|
|
t.Errorf("#%d: ver = %+v, want %+v", i, dver, tt.wdver)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-05-14 17:57:25 +03:00
|
|
|
|
|
|
|
func TestIsCompatibleWithVers(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
vers map[string]*version.Versions
|
|
|
|
local types.ID
|
|
|
|
minV, maxV *semver.Version
|
|
|
|
wok bool
|
|
|
|
}{
|
|
|
|
// too low
|
|
|
|
{
|
|
|
|
map[string]*version.Versions{
|
2015-08-22 04:52:16 +03:00
|
|
|
"a": {Server: "2.0.0", Cluster: "not_decided"},
|
|
|
|
"b": {Server: "2.1.0", Cluster: "2.1.0"},
|
|
|
|
"c": {Server: "2.1.0", Cluster: "2.1.0"},
|
2015-05-14 17:57:25 +03:00
|
|
|
},
|
|
|
|
0xa,
|
|
|
|
semver.Must(semver.NewVersion("2.0.0")), semver.Must(semver.NewVersion("2.0.0")),
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
map[string]*version.Versions{
|
2015-08-22 04:52:16 +03:00
|
|
|
"a": {Server: "2.1.0", Cluster: "not_decided"},
|
|
|
|
"b": {Server: "2.1.0", Cluster: "2.1.0"},
|
|
|
|
"c": {Server: "2.1.0", Cluster: "2.1.0"},
|
2015-05-14 17:57:25 +03:00
|
|
|
},
|
|
|
|
0xa,
|
|
|
|
semver.Must(semver.NewVersion("2.0.0")), semver.Must(semver.NewVersion("2.1.0")),
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
// too high
|
|
|
|
{
|
|
|
|
map[string]*version.Versions{
|
2015-08-22 04:52:16 +03:00
|
|
|
"a": {Server: "2.2.0", Cluster: "not_decided"},
|
|
|
|
"b": {Server: "2.0.0", Cluster: "2.0.0"},
|
|
|
|
"c": {Server: "2.0.0", Cluster: "2.0.0"},
|
2015-05-14 17:57:25 +03:00
|
|
|
},
|
|
|
|
0xa,
|
|
|
|
semver.Must(semver.NewVersion("2.1.0")), semver.Must(semver.NewVersion("2.2.0")),
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
// cannot get b's version, expect ok
|
|
|
|
{
|
|
|
|
map[string]*version.Versions{
|
2015-08-22 04:52:16 +03:00
|
|
|
"a": {Server: "2.1.0", Cluster: "not_decided"},
|
2015-05-14 17:57:25 +03:00
|
|
|
"b": nil,
|
2015-08-22 04:52:16 +03:00
|
|
|
"c": {Server: "2.1.0", Cluster: "2.1.0"},
|
2015-05-14 17:57:25 +03:00
|
|
|
},
|
|
|
|
0xa,
|
|
|
|
semver.Must(semver.NewVersion("2.0.0")), semver.Must(semver.NewVersion("2.1.0")),
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
// cannot get b and c's version, expect not ok
|
|
|
|
{
|
|
|
|
map[string]*version.Versions{
|
2015-08-22 04:52:16 +03:00
|
|
|
"a": {Server: "2.1.0", Cluster: "not_decided"},
|
2015-05-14 17:57:25 +03:00
|
|
|
"b": nil,
|
|
|
|
"c": nil,
|
|
|
|
},
|
|
|
|
0xa,
|
|
|
|
semver.Must(semver.NewVersion("2.0.0")), semver.Must(semver.NewVersion("2.1.0")),
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tt := range tests {
|
|
|
|
ok := isCompatibleWithVers(tt.vers, tt.local, tt.minV, tt.maxV)
|
|
|
|
if ok != tt.wok {
|
|
|
|
t.Errorf("#%d: ok = %+v, want %+v", i, ok, tt.wok)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|