diff --git a/api/go.mod b/api/go.mod index 39d24b22a..1af09c097 100644 --- a/api/go.mod +++ b/api/go.mod @@ -7,16 +7,20 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.2 github.com/grpc-ecosystem/grpc-gateway v1.16.0 + github.com/stretchr/testify v1.7.0 google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 google.golang.org/grpc v1.41.0 ) require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect golang.org/x/text v0.3.5 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) // Bad imports are sometimes causing attempts to pull that code. diff --git a/api/go.sum b/api/go.sum index e0d292fcd..106f5fd97 100644 --- a/api/go.sum +++ b/api/go.sum @@ -13,6 +13,7 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -52,12 +53,14 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -148,11 +151,13 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/api/version/version.go b/api/version/version.go index 6dce4d8ea..bd39791a9 100644 --- a/api/version/version.go +++ b/api/version/version.go @@ -33,6 +33,19 @@ var ( GitSHA = "Not provided (use ./build instead of go build)" ) +// Get all constant versions defined in a centralized place. +var ( + V3_0 = semver.Version{Major: 3, Minor: 0} + V3_1 = semver.Version{Major: 3, Minor: 1} + V3_2 = semver.Version{Major: 3, Minor: 2} + V3_3 = semver.Version{Major: 3, Minor: 3} + V3_4 = semver.Version{Major: 3, Minor: 4} + V3_5 = semver.Version{Major: 3, Minor: 5} + V3_6 = semver.Version{Major: 3, Minor: 6} + V3_7 = semver.Version{Major: 3, Minor: 7} + V4_0 = semver.Version{Major: 4, Minor: 0} +) + func init() { ver, err := semver.NewVersion(Version) if err == nil { @@ -55,3 +68,15 @@ func Cluster(v string) string { } return fmt.Sprintf("%s.%s", vs[0], vs[1]) } + +func Compare(ver1, ver2 semver.Version) int { + return ver1.Compare(ver2) +} + +func LessThan(ver1, ver2 semver.Version) bool { + return ver1.LessThan(ver2) +} + +func Equal(ver1, ver2 semver.Version) bool { + return ver1.Equal(ver2) +} diff --git a/api/version/version_test.go b/api/version/version_test.go new file mode 100644 index 000000000..532e7525a --- /dev/null +++ b/api/version/version_test.go @@ -0,0 +1,85 @@ +// Copyright 2022 The etcd Authors +// +// 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 version + +import ( + "testing" + + "github.com/coreos/go-semver/semver" + "github.com/stretchr/testify/assert" +) + +func TestVersionCompare(t *testing.T) { + cases := []struct { + name string + ver1 semver.Version + ver2 semver.Version + expectedCompareResult int + expectedLessThanResult bool + expectedEqualResult bool + }{ + { + name: "ver1 should be great than ver2", + ver1: V3_5, + ver2: V3_4, + expectedCompareResult: 1, + expectedLessThanResult: false, + expectedEqualResult: false, + }, + { + name: "ver1(4.0) should be great than ver2", + ver1: V4_0, + ver2: V3_7, + expectedCompareResult: 1, + expectedLessThanResult: false, + expectedEqualResult: false, + }, + { + name: "ver1 should be less than ver2", + ver1: V3_5, + ver2: V3_6, + expectedCompareResult: -1, + expectedLessThanResult: true, + expectedEqualResult: false, + }, + { + name: "ver1 should be less than ver2 (4.0)", + ver1: V3_5, + ver2: V4_0, + expectedCompareResult: -1, + expectedLessThanResult: true, + expectedEqualResult: false, + }, + { + name: "ver1 should be equal to ver2", + ver1: V3_5, + ver2: V3_5, + expectedCompareResult: 0, + expectedLessThanResult: false, + expectedEqualResult: true, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + compareResult := Compare(tc.ver1, tc.ver2) + lessThanResult := LessThan(tc.ver1, tc.ver2) + equalResult := Equal(tc.ver1, tc.ver2) + + assert.Equal(t, tc.expectedCompareResult, compareResult) + assert.Equal(t, tc.expectedLessThanResult, lessThanResult) + assert.Equal(t, tc.expectedEqualResult, equalResult) + }) + } +} diff --git a/etcdutl/etcdutl/migrate_command.go b/etcdutl/etcdutl/migrate_command.go index 87b10664f..521cf8ba8 100644 --- a/etcdutl/etcdutl/migrate_command.go +++ b/etcdutl/etcdutl/migrate_command.go @@ -22,6 +22,7 @@ import ( "github.com/spf13/cobra" "go.uber.org/zap" + "go.etcd.io/etcd/api/v3/version" "go.etcd.io/etcd/pkg/v3/cobrautl" "go.etcd.io/etcd/server/v3/storage/backend" "go.etcd.io/etcd/server/v3/storage/datadir" @@ -86,7 +87,7 @@ func (o *migrateOptions) Config() (*migrateConfig, error) { if err != nil { return nil, fmt.Errorf("failed to parse target version: %v", err) } - if c.targetVersion.LessThan(schema.V3_5) { + if c.targetVersion.LessThan(version.V3_5) { return nil, fmt.Errorf(`target version %q not supported. Minimal "3.5"`, storageVersionToString(c.targetVersion)) } @@ -143,7 +144,7 @@ func migrateForce(lg *zap.Logger, tx backend.BatchTx, target *semver.Version) { tx.LockOutsideApply() defer tx.Unlock() // Storage version is only supported since v3.6 - if target.LessThan(schema.V3_6) { + if target.LessThan(version.V3_6) { schema.UnsafeClearStorageVersion(tx) lg.Warn("forcefully cleared storage version") } else { diff --git a/server/etcdserver/version/monitor_test.go b/server/etcdserver/version/monitor_test.go index b11f09581..7d4e10884 100644 --- a/server/etcdserver/version/monitor_test.go +++ b/server/etcdserver/version/monitor_test.go @@ -12,12 +12,6 @@ import ( "go.etcd.io/etcd/api/v3/version" ) -var ( - V3_0 = semver.Version{Major: 3, Minor: 0} - V3_5 = semver.Version{Major: 3, Minor: 5} - V3_6 = semver.Version{Major: 3, Minor: 6} -) - func TestMemberMinimalVersion(t *testing.T) { tests := []struct { memberVersions map[string]*version.Versions @@ -69,25 +63,25 @@ func TestDecideStorageVersion(t *testing.T) { }, { name: "Should set storage version if cluster version is set", - clusterVersion: &V3_5, - expectStorageVersion: &V3_5, + clusterVersion: &version.V3_5, + expectStorageVersion: &version.V3_5, }, { name: "No action if storage version was already set", - storageVersion: &V3_5, - expectStorageVersion: &V3_5, + storageVersion: &version.V3_5, + expectStorageVersion: &version.V3_5, }, { name: "No action if storage version equals cluster version", - clusterVersion: &V3_5, - storageVersion: &V3_5, - expectStorageVersion: &V3_5, + clusterVersion: &version.V3_5, + storageVersion: &version.V3_5, + expectStorageVersion: &version.V3_5, }, { name: "Should set storage version to cluster version", - clusterVersion: &V3_6, - storageVersion: &V3_5, - expectStorageVersion: &V3_6, + clusterVersion: &version.V3_6, + storageVersion: &version.V3_5, + expectStorageVersion: &version.V3_6, }, } @@ -168,7 +162,7 @@ func TestUpdateClusterVersionIfNeeded(t *testing.T) { }{ { name: "Default to 3.0 if there are no members", - expectClusterVersion: &V3_0, + expectClusterVersion: &version.V3_0, }, { name: "Should pick lowest server version from members", @@ -176,7 +170,7 @@ func TestUpdateClusterVersionIfNeeded(t *testing.T) { "a": {Cluster: "3.7.0", Server: "3.6.0"}, "b": {Cluster: "3.4.0", Server: "3.5.0"}, }, - expectClusterVersion: &V3_5, + expectClusterVersion: &version.V3_5, }, { name: "Sets minimal version when member has broken version", @@ -184,7 +178,7 @@ func TestUpdateClusterVersionIfNeeded(t *testing.T) { "a": {Cluster: "3.7.0", Server: "3.6.0"}, "b": {Cluster: "xxxx", Server: "yyyy"}, }, - expectClusterVersion: &V3_0, + expectClusterVersion: &version.V3_0, }, { name: "Should pick lowest server version from members (cv already set)", @@ -192,8 +186,8 @@ func TestUpdateClusterVersionIfNeeded(t *testing.T) { "a": {Cluster: "3.7.0", Server: "3.6.0"}, "b": {Cluster: "3.4.0", Server: "3.5.0"}, }, - clusterVersion: &V3_5, - expectClusterVersion: &V3_5, + clusterVersion: &version.V3_5, + expectClusterVersion: &version.V3_5, }, { name: "Should upgrade cluster version if all members have upgraded (have higher server version)", @@ -201,8 +195,8 @@ func TestUpdateClusterVersionIfNeeded(t *testing.T) { "a": {Cluster: "3.5.0", Server: "3.6.0"}, "b": {Cluster: "3.5.0", Server: "3.6.0"}, }, - clusterVersion: &V3_5, - expectClusterVersion: &V3_6, + clusterVersion: &version.V3_5, + expectClusterVersion: &version.V3_6, }, { name: "Should downgrade cluster version if downgrade is set to allow older members to join", @@ -210,9 +204,9 @@ func TestUpdateClusterVersionIfNeeded(t *testing.T) { "a": {Cluster: "3.6.0", Server: "3.6.0"}, "b": {Cluster: "3.6.0", Server: "3.6.0"}, }, - clusterVersion: &V3_6, + clusterVersion: &version.V3_6, downgrade: &DowngradeInfo{TargetVersion: "3.5.0", Enabled: true}, - expectClusterVersion: &V3_5, + expectClusterVersion: &version.V3_5, }, { name: "Should maintain downgrade target version to allow older members to join", @@ -220,9 +214,9 @@ func TestUpdateClusterVersionIfNeeded(t *testing.T) { "a": {Cluster: "3.5.0", Server: "3.6.0"}, "b": {Cluster: "3.5.0", Server: "3.6.0"}, }, - clusterVersion: &V3_5, + clusterVersion: &version.V3_5, downgrade: &DowngradeInfo{TargetVersion: "3.5.0", Enabled: true}, - expectClusterVersion: &V3_5, + expectClusterVersion: &version.V3_5, }, { name: "Don't downgrade below supported range", @@ -230,9 +224,9 @@ func TestUpdateClusterVersionIfNeeded(t *testing.T) { "a": {Cluster: "3.5.0", Server: "3.6.0"}, "b": {Cluster: "3.5.0", Server: "3.6.0"}, }, - clusterVersion: &V3_5, + clusterVersion: &version.V3_5, downgrade: &DowngradeInfo{TargetVersion: "3.4.0", Enabled: true}, - expectClusterVersion: &V3_5, + expectClusterVersion: &version.V3_5, }, } @@ -335,25 +329,25 @@ func TestUpdateStorageVersionIfNeeded(t *testing.T) { }, { name: "Should set storage version if cluster version is set", - clusterVersion: &V3_5, - expectStorageVersion: &V3_5, + clusterVersion: &version.V3_5, + expectStorageVersion: &version.V3_5, }, { name: "No action if storage version was already set", - storageVersion: &V3_5, - expectStorageVersion: &V3_5, + storageVersion: &version.V3_5, + expectStorageVersion: &version.V3_5, }, { name: "No action if storage version equals cluster version", - clusterVersion: &V3_5, - storageVersion: &V3_5, - expectStorageVersion: &V3_5, + clusterVersion: &version.V3_5, + storageVersion: &version.V3_5, + expectStorageVersion: &version.V3_5, }, { name: "Should set storage version to cluster version", - clusterVersion: &V3_6, - storageVersion: &V3_5, - expectStorageVersion: &V3_6, + clusterVersion: &version.V3_6, + storageVersion: &version.V3_5, + expectStorageVersion: &version.V3_6, }, } diff --git a/server/etcdserver/version/version_test.go b/server/etcdserver/version/version_test.go index c82ca808c..93f490874 100644 --- a/server/etcdserver/version/version_test.go +++ b/server/etcdserver/version/version_test.go @@ -28,87 +28,83 @@ import ( "go.etcd.io/etcd/api/v3/version" ) -var ( - V3_7 = semver.Version{Major: 3, Minor: 7} -) - func TestUpgradeSingleNode(t *testing.T) { lg := zaptest.NewLogger(t) - c := newCluster(lg, 1, V3_6) + c := newCluster(lg, 1, version.V3_6) c.StepMonitors() - assert.Equal(t, newCluster(lg, 1, V3_6), c) + assert.Equal(t, newCluster(lg, 1, version.V3_6), c) - c.ReplaceMemberBinary(0, V3_7) + c.ReplaceMemberBinary(0, version.V3_7) c.StepMonitors() c.StepMonitors() - assert.Equal(t, newCluster(lg, 1, V3_7), c) + assert.Equal(t, newCluster(lg, 1, version.V3_7), c) } func TestUpgradeThreeNodes(t *testing.T) { lg := zaptest.NewLogger(t) - c := newCluster(lg, 3, V3_6) + c := newCluster(lg, 3, version.V3_6) c.StepMonitors() - assert.Equal(t, newCluster(lg, 3, V3_6), c) + assert.Equal(t, newCluster(lg, 3, version.V3_6), c) - c.ReplaceMemberBinary(0, V3_7) + c.ReplaceMemberBinary(0, version.V3_7) c.StepMonitors() - c.ReplaceMemberBinary(1, V3_7) + c.ReplaceMemberBinary(1, version.V3_7) c.StepMonitors() - c.ReplaceMemberBinary(2, V3_7) + c.ReplaceMemberBinary(2, version.V3_7) c.StepMonitors() c.StepMonitors() - assert.Equal(t, newCluster(lg, 3, V3_7), c) + assert.Equal(t, newCluster(lg, 3, version.V3_7), c) } func TestDowngradeSingleNode(t *testing.T) { lg := zaptest.NewLogger(t) - c := newCluster(lg, 1, V3_6) + c := newCluster(lg, 1, version.V3_6) c.StepMonitors() - assert.Equal(t, newCluster(lg, 1, V3_6), c) + assert.Equal(t, newCluster(lg, 1, version.V3_6), c) - assert.NoError(t, c.Version().DowngradeEnable(context.Background(), &V3_5)) + assert.NoError(t, c.Version().DowngradeEnable(context.Background(), &version.V3_5)) c.StepMonitors() - assert.Equal(t, V3_5, c.clusterVersion) + assert.Equal(t, version.V3_5, c.clusterVersion) - c.ReplaceMemberBinary(0, V3_5) + c.ReplaceMemberBinary(0, version.V3_5) c.StepMonitors() - assert.Equal(t, newCluster(lg, 1, V3_5), c) + assert.Equal(t, newCluster(lg, 1, version.V3_5), c) } func TestDowngradeThreeNode(t *testing.T) { lg := zaptest.NewLogger(t) - c := newCluster(lg, 3, V3_6) + c := newCluster(lg, 3, version.V3_6) c.StepMonitors() - assert.Equal(t, newCluster(lg, 3, V3_6), c) + assert.Equal(t, newCluster(lg, 3, version.V3_6), c) - assert.NoError(t, c.Version().DowngradeEnable(context.Background(), &V3_5)) + assert.NoError(t, c.Version().DowngradeEnable(context.Background(), &version.V3_5)) c.StepMonitors() - assert.Equal(t, V3_5, c.clusterVersion) + assert.Equal(t, version.V3_5, c.clusterVersion) - c.ReplaceMemberBinary(0, V3_5) + c.ReplaceMemberBinary(0, version.V3_5) c.StepMonitors() - c.ReplaceMemberBinary(1, V3_5) + c.ReplaceMemberBinary(1, version.V3_5) c.StepMonitors() - c.ReplaceMemberBinary(2, V3_5) + c.ReplaceMemberBinary(2, version.V3_5) c.StepMonitors() - assert.Equal(t, newCluster(lg, 3, V3_5), c) + assert.Equal(t, newCluster(lg, 3, version.V3_5), c) } func TestNewerMemberCanReconnectDuringDowngrade(t *testing.T) { lg := zaptest.NewLogger(t) - c := newCluster(lg, 3, V3_6) + c := newCluster(lg, 3, version.V3_6) c.StepMonitors() - assert.Equal(t, newCluster(lg, 3, V3_6), c) + assert.Equal(t, newCluster(lg, 3, version.V3_6), c) - assert.NoError(t, c.Version().DowngradeEnable(context.Background(), &V3_5)) + assert.NoError(t, c.Version().DowngradeEnable(context.Background(), &version.V3_5)) c.StepMonitors() - assert.Equal(t, V3_5, c.clusterVersion) + assert.Equal(t, version.V3_5, c.clusterVersion) - c.ReplaceMemberBinary(0, V3_5) + c.ReplaceMemberBinary(0, version.V3_5) c.StepMonitors() c.MemberCrashes(2) @@ -116,12 +112,12 @@ func TestNewerMemberCanReconnectDuringDowngrade(t *testing.T) { c.MemberReconnects(2) c.StepMonitors() - c.ReplaceMemberBinary(1, V3_5) + c.ReplaceMemberBinary(1, version.V3_5) c.StepMonitors() - c.ReplaceMemberBinary(2, V3_5) + c.ReplaceMemberBinary(2, version.V3_5) c.StepMonitors() - assert.Equal(t, newCluster(lg, 3, V3_5), c) + assert.Equal(t, newCluster(lg, 3, version.V3_5), c) } func newCluster(lg *zap.Logger, memberCount int, ver semver.Version) *clusterMock { diff --git a/server/lease/lessor.go b/server/lease/lessor.go index f127b5d9e..1318552dd 100644 --- a/server/lease/lessor.go +++ b/server/lease/lessor.go @@ -25,6 +25,7 @@ import ( "github.com/coreos/go-semver/semver" pb "go.etcd.io/etcd/api/v3/etcdserverpb" + "go.etcd.io/etcd/api/v3/version" "go.etcd.io/etcd/server/v3/lease/leasepb" "go.etcd.io/etcd/server/v3/storage/backend" "go.etcd.io/etcd/server/v3/storage/schema" @@ -37,8 +38,6 @@ const NoLease = LeaseID(0) // MaxLeaseTTL is the maximum lease TTL value const MaxLeaseTTL = 9000000000 -var v3_6 = semver.Version{Major: 3, Minor: 6} - var ( forever = time.Time{} @@ -381,11 +380,11 @@ func (le *lessor) Checkpoint(id LeaseID, remainingTTL int64) error { func (le *lessor) shouldPersistCheckpoints() bool { cv := le.cluster.Version() - return le.checkpointPersist || (cv != nil && greaterOrEqual(*cv, v3_6)) + return le.checkpointPersist || (cv != nil && greaterOrEqual(*cv, version.V3_6)) } func greaterOrEqual(first, second semver.Version) bool { - return !first.LessThan(second) + return !version.LessThan(first, second) } // Renew renews an existing lease. If the given lease does not exist or diff --git a/server/storage/schema/migration.go b/server/storage/schema/migration.go index 61ea51bf2..df902a2f0 100644 --- a/server/storage/schema/migration.go +++ b/server/storage/schema/migration.go @@ -18,6 +18,7 @@ import ( "fmt" "github.com/coreos/go-semver/semver" + "go.etcd.io/etcd/api/v3/version" "go.etcd.io/etcd/server/v3/storage/backend" "go.uber.org/zap" ) @@ -102,7 +103,7 @@ func (s migrationStep) unsafeExecute(lg *zap.Logger, tx backend.BatchTx) error { return err } // Storage version is available since v3.6, downgrading target v3.5 should clean this field. - if !s.target.LessThan(V3_6) { + if !s.target.LessThan(version.V3_6) { UnsafeSetStorageVersion(tx, &s.target) } return nil diff --git a/server/storage/schema/migration_test.go b/server/storage/schema/migration_test.go index 95e4d9739..b1a510448 100644 --- a/server/storage/schema/migration_test.go +++ b/server/storage/schema/migration_test.go @@ -21,15 +21,12 @@ import ( "github.com/coreos/go-semver/semver" "github.com/stretchr/testify/assert" + "go.etcd.io/etcd/api/v3/version" "go.etcd.io/etcd/server/v3/storage/backend" betesting "go.etcd.io/etcd/server/v3/storage/backend/testing" "go.uber.org/zap/zaptest" ) -var ( - V4_0 = semver.Version{Major: 4, Minor: 0} -) - func TestNewPlan(t *testing.T) { tcs := []struct { name string @@ -42,25 +39,25 @@ func TestNewPlan(t *testing.T) { }{ { name: "Update v3.5 to v3.6 should work", - current: V3_5, - target: V3_6, + current: version.V3_5, + target: version.V3_6, }, { name: "Downgrade v3.6 to v3.5 should fail as downgrades are not yet supported", - current: V3_6, - target: V3_5, + current: version.V3_6, + target: version.V3_5, }, { name: "Upgrade v3.6 to v3.7 should fail as v3.7 is unknown", - current: V3_6, - target: V3_7, + current: version.V3_6, + target: version.V3_7, expectError: true, expectErrorMsg: `version "3.7.0" is not supported`, }, { name: "Upgrade v3.6 to v4.0 as major version changes are unsupported", - current: V3_6, - target: V4_0, + current: version.V3_6, + target: version.V4_0, expectError: true, expectErrorMsg: "changing major storage version is not supported", }, @@ -152,7 +149,7 @@ func TestMigrationStepExecute(t *testing.T) { { name: "Downgrade below to below v3.6 doesn't leave storage version as it was not supported then", currentVersion: semver.Version{Major: 3, Minor: 6}, - changes: schemaChanges[V3_6], + changes: schemaChanges[version.V3_6], isUpgrade: false, expectVersion: nil, }, diff --git a/server/storage/schema/schema.go b/server/storage/schema/schema.go index 68bb212d7..b0134cfe9 100644 --- a/server/storage/schema/schema.go +++ b/server/storage/schema/schema.go @@ -24,11 +24,6 @@ import ( "go.etcd.io/etcd/server/v3/storage/backend" ) -var ( - V3_5 = semver.Version{Major: 3, Minor: 5} - V3_6 = semver.Version{Major: 3, Minor: 6} -) - // Validate checks provided backend to confirm that schema used is supported. func Validate(lg *zap.Logger, tx backend.ReadTx) error { tx.Lock() @@ -108,7 +103,7 @@ func UnsafeDetectSchemaVersion(lg *zap.Logger, tx backend.ReadTx) (v semver.Vers if term == 0 { return v, fmt.Errorf("missing term information") } - return V3_5, nil + return version.V3_5, nil } func schemaChangesForVersion(v semver.Version, isUpgrade bool) ([]schemaChange, error) { @@ -132,7 +127,7 @@ var ( // schemaChanges list changes that were introduced in a particular version. // schema was introduced in v3.6 as so its changes were not tracked before. schemaChanges = map[semver.Version][]schemaChange{ - V3_6: { + version.V3_6: { addNewField(Meta, MetaStorageVersionName, emptyStorageVersion), }, } diff --git a/server/storage/schema/schema_test.go b/server/storage/schema/schema_test.go index 3272b1893..59fb3a1af 100644 --- a/server/storage/schema/schema_test.go +++ b/server/storage/schema/schema_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/assert" "go.etcd.io/etcd/api/v3/etcdserverpb" "go.etcd.io/etcd/api/v3/membershippb" + "go.etcd.io/etcd/api/v3/version" "go.etcd.io/etcd/raft/v3/raftpb" "go.etcd.io/etcd/server/v3/storage/backend" betesting "go.etcd.io/etcd/server/v3/storage/backend/testing" @@ -30,11 +31,6 @@ import ( "go.uber.org/zap" ) -var ( - V3_4 = semver.Version{Major: 3, Minor: 4} - V3_7 = semver.Version{Major: 3, Minor: 7} -) - func TestValidate(t *testing.T) { tcs := []struct { name string @@ -48,23 +44,23 @@ func TestValidate(t *testing.T) { // For storage to be considered v3.5 it have both confstate and term key set. { name: `V3.4 schema is correct`, - version: V3_4, + version: version.V3_4, }, { name: `V3.5 schema without confstate and term fields is correct`, - version: V3_5, + version: version.V3_5, overrideKeys: func(tx backend.BatchTx) {}, }, { name: `V3.5 schema without term field is correct`, - version: V3_5, + version: version.V3_5, overrideKeys: func(tx backend.BatchTx) { MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{}) }, }, { name: `V3.5 schema with all fields is correct`, - version: V3_5, + version: version.V3_5, overrideKeys: func(tx backend.BatchTx) { MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{}) UnsafeUpdateConsistentIndex(tx, 1, 1) @@ -72,11 +68,11 @@ func TestValidate(t *testing.T) { }, { name: `V3.6 schema is correct`, - version: V3_6, + version: version.V3_6, }, { name: `V3.7 schema is unknown and should return error`, - version: V3_7, + version: version.V3_7, expectError: true, expectErrorMsg: `version "3.7.0" is not supported`, }, @@ -116,68 +112,68 @@ func TestMigrate(t *testing.T) { // For storage to be considered v3.5 it have both confstate and term key set. { name: `Upgrading v3.5 to v3.6 should be rejected if confstate is not set`, - version: V3_5, + version: version.V3_5, overrideKeys: func(tx backend.BatchTx) {}, - targetVersion: V3_6, + targetVersion: version.V3_6, expectVersion: nil, expectError: true, expectErrorMsg: `cannot detect storage schema version: missing confstate information`, }, { name: `Upgrading v3.5 to v3.6 should be rejected if term is not set`, - version: V3_5, + version: version.V3_5, overrideKeys: func(tx backend.BatchTx) { MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{}) }, - targetVersion: V3_6, + targetVersion: version.V3_6, expectVersion: nil, expectError: true, expectErrorMsg: `cannot detect storage schema version: missing term information`, }, { name: `Upgrading v3.5 to v3.6 should succeed; all required fields are set`, - version: V3_5, - targetVersion: V3_6, - expectVersion: &V3_6, + version: version.V3_5, + targetVersion: version.V3_6, + expectVersion: &version.V3_6, }, { name: `Migrate on same v3.5 version passes and doesn't set storage version'`, - version: V3_5, - targetVersion: V3_5, + version: version.V3_5, + targetVersion: version.V3_5, expectVersion: nil, }, { name: `Migrate on same v3.6 version passes`, - version: V3_6, - targetVersion: V3_6, - expectVersion: &V3_6, + version: version.V3_6, + targetVersion: version.V3_6, + expectVersion: &version.V3_6, }, { name: `Migrate on same v3.7 version passes`, - version: V3_7, - targetVersion: V3_7, - expectVersion: &V3_7, + version: version.V3_7, + targetVersion: version.V3_7, + expectVersion: &version.V3_7, }, { name: "Upgrading 3.6 to v3.7 is not supported", - version: V3_6, - targetVersion: V3_7, - expectVersion: &V3_6, + version: version.V3_6, + targetVersion: version.V3_7, + expectVersion: &version.V3_6, expectError: true, expectErrorMsg: `cannot create migration plan: version "3.7.0" is not supported`, }, { name: "Downgrading v3.7 to v3.6 is not supported", - version: V3_7, - targetVersion: V3_6, - expectVersion: &V3_7, + version: version.V3_7, + targetVersion: version.V3_6, + expectVersion: &version.V3_7, expectError: true, expectErrorMsg: `cannot create migration plan: version "3.7.0" is not supported`, }, { name: "Downgrading v3.6 to v3.5 works as there are no v3.6 wal entries", - version: V3_6, - targetVersion: V3_5, + version: version.V3_6, + targetVersion: version.V3_5, walEntries: []etcdserverpb.InternalRaftRequest{ {Range: &etcdserverpb.RangeRequest{Key: []byte("\x00"), RangeEnd: []byte("\xff")}}, }, @@ -185,19 +181,19 @@ func TestMigrate(t *testing.T) { }, { name: "Downgrading v3.6 to v3.5 fails if there are newer WAL entries", - version: V3_6, - targetVersion: V3_5, + version: version.V3_6, + targetVersion: version.V3_5, walEntries: []etcdserverpb.InternalRaftRequest{ {ClusterVersionSet: &membershippb.ClusterVersionSetRequest{Ver: "3.6.0"}}, }, - expectVersion: &V3_6, + expectVersion: &version.V3_6, expectError: true, expectErrorMsg: "cannot downgrade storage, WAL contains newer entries", }, { name: "Downgrading v3.5 to v3.4 is not supported as schema was introduced in v3.6", - version: V3_5, - targetVersion: V3_4, + version: version.V3_5, + targetVersion: version.V3_4, expectVersion: nil, expectError: true, expectErrorMsg: `cannot create migration plan: version "3.5.0" is not supported`, @@ -236,7 +232,7 @@ func TestMigrateIsReversible(t *testing.T) { state map[string]string }{ { - initialVersion: V3_5, + initialVersion: version.V3_5, state: map[string]string{ "confState": `{"auto_leave":false}`, "consistent_index": "\x00\x00\x00\x00\x00\x00\x00\x01", @@ -244,7 +240,7 @@ func TestMigrateIsReversible(t *testing.T) { }, }, { - initialVersion: V3_6, + initialVersion: version.V3_6, state: map[string]string{ "confState": `{"auto_leave":false}`, "consistent_index": "\x00\x00\x00\x00\x00\x00\x00\x01", @@ -297,7 +293,7 @@ func TestMigrateIsReversible(t *testing.T) { } } -func setupBackendData(t *testing.T, version semver.Version, overrideKeys func(tx backend.BatchTx)) string { +func setupBackendData(t *testing.T, ver semver.Version, overrideKeys func(tx backend.BatchTx)) string { t.Helper() be, tmpPath := betesting.NewTmpBackend(t, time.Microsecond, 10) tx := be.BatchTx() @@ -309,19 +305,19 @@ func setupBackendData(t *testing.T, version semver.Version, overrideKeys func(tx if overrideKeys != nil { overrideKeys(tx) } else { - switch version { - case V3_4: - case V3_5: + switch ver { + case version.V3_4: + case version.V3_5: MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{}) UnsafeUpdateConsistentIndex(tx, 1, 1) - case V3_6: + case version.V3_6: MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{}) UnsafeUpdateConsistentIndex(tx, 1, 1) - UnsafeSetStorageVersion(tx, &V3_6) - case V3_7: + UnsafeSetStorageVersion(tx, &version.V3_6) + case version.V3_7: MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{}) UnsafeUpdateConsistentIndex(tx, 1, 1) - UnsafeSetStorageVersion(tx, &V3_7) + UnsafeSetStorageVersion(tx, &version.V3_7) tx.UnsafePut(Meta, []byte("future-key"), []byte("")) default: t.Fatalf("Unsupported storage version") diff --git a/server/storage/wal/version_test.go b/server/storage/wal/version_test.go index ace1ac5e3..e725c876d 100644 --- a/server/storage/wal/version_test.go +++ b/server/storage/wal/version_test.go @@ -23,20 +23,12 @@ import ( "github.com/stretchr/testify/assert" "go.etcd.io/etcd/api/v3/etcdserverpb" "go.etcd.io/etcd/api/v3/membershippb" + "go.etcd.io/etcd/api/v3/version" "go.etcd.io/etcd/pkg/v3/pbutil" "go.etcd.io/etcd/raft/v3/raftpb" "google.golang.org/protobuf/reflect/protoreflect" ) -var ( - V3_0 = semver.Version{Major: 3, Minor: 0} - V3_1 = semver.Version{Major: 3, Minor: 1} - V3_3 = semver.Version{Major: 3, Minor: 3} - V3_4 = semver.Version{Major: 3, Minor: 4} - V3_5 = semver.Version{Major: 3, Minor: 5} - V3_6 = semver.Version{Major: 3, Minor: 6} -) - func TestEtcdVersionFromEntry(t *testing.T) { raftReq := etcdserverpb.InternalRaftRequest{Header: &etcdserverpb.RequestHeader{AuthRevision: 1}} normalRequestData := pbutil.MustMarshal(&raftReq) @@ -63,7 +55,7 @@ func TestEtcdVersionFromEntry(t *testing.T) { Type: raftpb.EntryNormal, Data: normalRequestData, }, - expect: &V3_1, + expect: &version.V3_1, }, { name: "Setting cluster version implies version within", @@ -73,7 +65,7 @@ func TestEtcdVersionFromEntry(t *testing.T) { Type: raftpb.EntryNormal, Data: clusterVersionV3_6Data, }, - expect: &V3_6, + expect: &version.V3_6, }, { name: "Using ConfigChange implies v3.4", @@ -83,7 +75,7 @@ func TestEtcdVersionFromEntry(t *testing.T) { Type: raftpb.EntryConfChange, Data: confChangeData, }, - expect: &V3_0, + expect: &version.V3_0, }, { name: "Using ConfigChangeV2 implies v3.4", @@ -93,7 +85,7 @@ func TestEtcdVersionFromEntry(t *testing.T) { Type: raftpb.EntryConfChangeV2, Data: confChangeV2Data, }, - expect: &V3_4, + expect: &version.V3_4, }, } for _, tc := range tcs { @@ -118,52 +110,52 @@ func TestEtcdVersionFromMessage(t *testing.T) { { name: "Empty RequestHeader impies v3.0", input: &etcdserverpb.RequestHeader{}, - expect: &V3_0, + expect: &version.V3_0, }, { name: "RequestHeader AuthRevision field set implies v3.5", input: &etcdserverpb.RequestHeader{AuthRevision: 1}, - expect: &V3_1, + expect: &version.V3_1, }, { name: "RequestHeader Username set implies v3.0", input: &etcdserverpb.RequestHeader{Username: "Alice"}, - expect: &V3_0, + expect: &version.V3_0, }, { name: "When two fields are set take higher version", input: &etcdserverpb.RequestHeader{AuthRevision: 1, Username: "Alice"}, - expect: &V3_1, + expect: &version.V3_1, }, { name: "Setting a RequestHeader AuthRevision in subfield implies v3.1", input: &etcdserverpb.InternalRaftRequest{Header: &etcdserverpb.RequestHeader{AuthRevision: 1}}, - expect: &V3_1, + expect: &version.V3_1, }, { name: "Setting a DowngradeInfoSetRequest implies v3.5", input: &etcdserverpb.InternalRaftRequest{DowngradeInfoSet: &membershippb.DowngradeInfoSetRequest{}}, - expect: &V3_5, + expect: &version.V3_5, }, { name: "Enum CompareResult set to EQUAL implies v3.0", input: &etcdserverpb.Compare{Result: etcdserverpb.Compare_EQUAL}, - expect: &V3_0, + expect: &version.V3_0, }, { name: "Enum CompareResult set to NOT_EQUAL implies v3.1", input: &etcdserverpb.Compare{Result: etcdserverpb.Compare_NOT_EQUAL}, - expect: &V3_1, + expect: &version.V3_1, }, { name: "Oneof Compare version set implies v3.1", input: &etcdserverpb.Compare{TargetUnion: &etcdserverpb.Compare_Version{}}, - expect: &V3_0, + expect: &version.V3_0, }, { name: "Oneof Compare lease set implies v3.3", input: &etcdserverpb.Compare{TargetUnion: &etcdserverpb.Compare_Lease{}}, - expect: &V3_3, + expect: &version.V3_3, }, } for _, tc := range tcs { @@ -195,55 +187,55 @@ func TestEtcdVersionFromFieldOptionsString(t *testing.T) { }, { input: `[versionpb.etcd_version_msg]:"3.5"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `[versionpb.etcd_version_enum]:"3.5"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `[versionpb.etcd_version_field]:"3.5"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `[versionpb.etcd_version_enum_value]:"3.5"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `65001:0 [versionpb.etcd_version_msg]:"3.5"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `65004:"NodeID" [versionpb.etcd_version_msg]:"3.5"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `65004:"NodeID" [versionpb.etcd_version_enum]:"3.5"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `[versionpb.other_field]:"NodeID" [versionpb.etcd_version_msg]:"3.5"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `[versionpb.etcd_version_msg]:"3.5" 65001:0`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `[versionpb.etcd_version_msg]:"3.5" 65004:"NodeID"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `[versionpb.etcd_version_msg]:"3.5" [versionpb.other_field]:"NodeID"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `[versionpb.other_field]:"NodeID" [versionpb.etcd_version_msg]:"3.5" [versionpb.another_field]:"NodeID"`, - expect: &V3_5, + expect: &version.V3_5, }, { input: `65001:0 [versionpb.etcd_version_msg]:"3.5" 65001:0"`, - expect: &V3_5, + expect: &version.V3_5, }, } for _, tc := range tcs { @@ -265,24 +257,24 @@ func TestMaxVersion(t *testing.T) { expect: nil, }, { - a: &V3_5, + a: &version.V3_5, b: nil, - expect: &V3_5, + expect: &version.V3_5, }, { a: nil, - b: &V3_5, - expect: &V3_5, + b: &version.V3_5, + expect: &version.V3_5, }, { - a: &V3_6, - b: &V3_5, - expect: &V3_6, + a: &version.V3_6, + b: &version.V3_5, + expect: &version.V3_6, }, { - a: &V3_5, - b: &V3_6, - expect: &V3_6, + a: &version.V3_5, + b: &version.V3_6, + expect: &version.V3_6, }, } for _, tc := range tcs { diff --git a/tests/e2e/utl_migrate_test.go b/tests/e2e/utl_migrate_test.go index 9a7408665..46378e8a4 100644 --- a/tests/e2e/utl_migrate_test.go +++ b/tests/e2e/utl_migrate_test.go @@ -22,6 +22,7 @@ import ( "github.com/coreos/go-semver/semver" "github.com/stretchr/testify/assert" + "go.etcd.io/etcd/api/v3/version" "go.etcd.io/etcd/client/pkg/v3/fileutil" "go.etcd.io/etcd/server/v3/storage/backend" "go.etcd.io/etcd/server/v3/storage/schema" @@ -45,25 +46,25 @@ func TestEtctlutlMigrate(t *testing.T) { name: "Invalid target version string", targetVersion: "abc", expectLogsSubString: `Error: wrong target version format, expected "X.Y", got "abc"`, - expectStorageVersion: &schema.V3_6, + expectStorageVersion: &version.V3_6, }, { name: "Invalid target version", targetVersion: "3.a", expectLogsSubString: `Error: failed to parse target version: strconv.ParseInt: parsing "a": invalid syntax`, - expectStorageVersion: &schema.V3_6, + expectStorageVersion: &version.V3_6, }, { name: "Target with only major version is invalid", targetVersion: "3", expectLogsSubString: `Error: wrong target version format, expected "X.Y", got "3"`, - expectStorageVersion: &schema.V3_6, + expectStorageVersion: &version.V3_6, }, { name: "Target with patch version is invalid", targetVersion: "3.6.0", expectLogsSubString: `Error: wrong target version format, expected "X.Y", got "3.6.0"`, - expectStorageVersion: &schema.V3_6, + expectStorageVersion: &version.V3_6, }, { name: "Migrate v3.5 to v3.5 is no-op", @@ -75,19 +76,19 @@ func TestEtctlutlMigrate(t *testing.T) { name: "Upgrade v3.5 to v3.6 should work", binary: lastReleaseBinary, targetVersion: "3.6", - expectStorageVersion: &schema.V3_6, + expectStorageVersion: &version.V3_6, }, { name: "Migrate v3.6 to v3.6 is no-op", targetVersion: "3.6", expectLogsSubString: "storage version up-to-date\t" + `{"storage-version": "3.6"}`, - expectStorageVersion: &schema.V3_6, + expectStorageVersion: &version.V3_6, }, { name: "Downgrade v3.6 to v3.5 should fail until it's implemented", targetVersion: "3.5", expectLogsSubString: "cannot downgrade storage, WAL contains newer entries", - expectStorageVersion: &schema.V3_6, + expectStorageVersion: &version.V3_6, }, { name: "Downgrade v3.6 to v3.5 with force should work",