mirror of
				https://github.com/etcd-io/etcd.git
				synced 2024-09-27 06:25:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			288 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2021 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 wal
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/coreos/go-semver/semver"
 | |
| 	"github.com/golang/protobuf/proto"
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"google.golang.org/protobuf/reflect/protoreflect"
 | |
| 
 | |
| 	"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/raft/v3/raftpb"
 | |
| )
 | |
| 
 | |
| func TestEtcdVersionFromEntry(t *testing.T) {
 | |
| 	raftReq := etcdserverpb.InternalRaftRequest{Header: &etcdserverpb.RequestHeader{AuthRevision: 1}}
 | |
| 	normalRequestData := pbutil.MustMarshal(&raftReq)
 | |
| 
 | |
| 	clusterVersionV3_6Req := etcdserverpb.InternalRaftRequest{ClusterVersionSet: &membershippb.ClusterVersionSetRequest{Ver: "3.6.0"}}
 | |
| 	clusterVersionV3_6Data := pbutil.MustMarshal(&clusterVersionV3_6Req)
 | |
| 
 | |
| 	confChange := raftpb.ConfChange{Type: raftpb.ConfChangeAddLearnerNode}
 | |
| 	confChangeData := pbutil.MustMarshal(&confChange)
 | |
| 
 | |
| 	confChangeV2 := raftpb.ConfChangeV2{Transition: raftpb.ConfChangeTransitionJointExplicit}
 | |
| 	confChangeV2Data := pbutil.MustMarshal(&confChangeV2)
 | |
| 
 | |
| 	tcs := []struct {
 | |
| 		name   string
 | |
| 		input  raftpb.Entry
 | |
| 		expect *semver.Version
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "Using RequestHeader AuthRevision in NormalEntry implies v3.1",
 | |
| 			input: raftpb.Entry{
 | |
| 				Term:  1,
 | |
| 				Index: 2,
 | |
| 				Type:  raftpb.EntryNormal,
 | |
| 				Data:  normalRequestData,
 | |
| 			},
 | |
| 			expect: &version.V3_1,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Setting cluster version implies version within",
 | |
| 			input: raftpb.Entry{
 | |
| 				Term:  1,
 | |
| 				Index: 2,
 | |
| 				Type:  raftpb.EntryNormal,
 | |
| 				Data:  clusterVersionV3_6Data,
 | |
| 			},
 | |
| 			expect: &version.V3_6,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Using ConfigChange implies v3.0",
 | |
| 			input: raftpb.Entry{
 | |
| 				Term:  1,
 | |
| 				Index: 2,
 | |
| 				Type:  raftpb.EntryConfChange,
 | |
| 				Data:  confChangeData,
 | |
| 			},
 | |
| 			expect: &version.V3_0,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "Using ConfigChangeV2 implies v3.4",
 | |
| 			input: raftpb.Entry{
 | |
| 				Term:  1,
 | |
| 				Index: 2,
 | |
| 				Type:  raftpb.EntryConfChangeV2,
 | |
| 				Data:  confChangeV2Data,
 | |
| 			},
 | |
| 			expect: &version.V3_4,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tc := range tcs {
 | |
| 		t.Run(tc.name, func(t *testing.T) {
 | |
| 			var maxVer *semver.Version
 | |
| 			err := visitEntry(tc.input, func(path protoreflect.FullName, ver *semver.Version) error {
 | |
| 				maxVer = maxVersion(maxVer, ver)
 | |
| 				return nil
 | |
| 			})
 | |
| 			assert.NoError(t, err)
 | |
| 			assert.Equal(t, tc.expect, maxVer)
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestEtcdVersionFromMessage(t *testing.T) {
 | |
| 	tcs := []struct {
 | |
| 		name   string
 | |
| 		input  proto.Message
 | |
| 		expect *semver.Version
 | |
| 	}{
 | |
| 		{
 | |
| 			name:   "Empty RequestHeader impies v3.0",
 | |
| 			input:  &etcdserverpb.RequestHeader{},
 | |
| 			expect: &version.V3_0,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "RequestHeader AuthRevision field set implies v3.5",
 | |
| 			input:  &etcdserverpb.RequestHeader{AuthRevision: 1},
 | |
| 			expect: &version.V3_1,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "RequestHeader Username set implies v3.0",
 | |
| 			input:  &etcdserverpb.RequestHeader{Username: "Alice"},
 | |
| 			expect: &version.V3_0,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "When two fields are set take higher version",
 | |
| 			input:  &etcdserverpb.RequestHeader{AuthRevision: 1, Username: "Alice"},
 | |
| 			expect: &version.V3_1,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "Setting a RequestHeader AuthRevision in subfield implies v3.1",
 | |
| 			input:  &etcdserverpb.InternalRaftRequest{Header: &etcdserverpb.RequestHeader{AuthRevision: 1}},
 | |
| 			expect: &version.V3_1,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "Setting a DowngradeInfoSetRequest implies v3.5",
 | |
| 			input:  &etcdserverpb.InternalRaftRequest{DowngradeInfoSet: &membershippb.DowngradeInfoSetRequest{}},
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "Enum CompareResult set to EQUAL implies v3.0",
 | |
| 			input:  &etcdserverpb.Compare{Result: etcdserverpb.Compare_EQUAL},
 | |
| 			expect: &version.V3_0,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "Enum CompareResult set to NOT_EQUAL implies v3.1",
 | |
| 			input:  &etcdserverpb.Compare{Result: etcdserverpb.Compare_NOT_EQUAL},
 | |
| 			expect: &version.V3_1,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "Oneof Compare version set implies v3.1",
 | |
| 			input:  &etcdserverpb.Compare{TargetUnion: &etcdserverpb.Compare_Version{}},
 | |
| 			expect: &version.V3_0,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "Oneof Compare lease set implies v3.3",
 | |
| 			input:  &etcdserverpb.Compare{TargetUnion: &etcdserverpb.Compare_Lease{}},
 | |
| 			expect: &version.V3_3,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tc := range tcs {
 | |
| 		t.Run(tc.name, func(t *testing.T) {
 | |
| 			var maxVer *semver.Version
 | |
| 			err := visitMessage(proto.MessageReflect(tc.input), func(path protoreflect.FullName, ver *semver.Version) error {
 | |
| 				maxVer = maxVersion(maxVer, ver)
 | |
| 				return nil
 | |
| 			})
 | |
| 			assert.NoError(t, err)
 | |
| 			assert.Equal(t, tc.expect, maxVer)
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestEtcdVersionFromFieldOptionsString(t *testing.T) {
 | |
| 	tcs := []struct {
 | |
| 		input  string
 | |
| 		expect *semver.Version
 | |
| 	}{
 | |
| 		{
 | |
| 			input: "65001:0",
 | |
| 		},
 | |
| 		{
 | |
| 			input: `65001:0  65004:"NodeID"`,
 | |
| 		},
 | |
| 		{
 | |
| 			input: `[versionpb.XXX]:"3.5"`,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `[versionpb.etcd_version_msg]:"3.5"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `[versionpb.etcd_version_enum]:"3.5"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `[versionpb.etcd_version_field]:"3.5"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `[versionpb.etcd_version_enum_value]:"3.5"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `65001:0 [versionpb.etcd_version_msg]:"3.5"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `65004:"NodeID" [versionpb.etcd_version_msg]:"3.5"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `65004:"NodeID" [versionpb.etcd_version_enum]:"3.5"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `[versionpb.other_field]:"NodeID" [versionpb.etcd_version_msg]:"3.5"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `[versionpb.etcd_version_msg]:"3.5" 65001:0`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `[versionpb.etcd_version_msg]:"3.5" 65004:"NodeID"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `[versionpb.etcd_version_msg]:"3.5" [versionpb.other_field]:"NodeID"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `[versionpb.other_field]:"NodeID" [versionpb.etcd_version_msg]:"3.5" [versionpb.another_field]:"NodeID"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			input:  `65001:0 [versionpb.etcd_version_msg]:"3.5" 65001:0"`,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tc := range tcs {
 | |
| 		t.Run(tc.input, func(t *testing.T) {
 | |
| 			ver, err := etcdVersionFromOptionsString(tc.input)
 | |
| 			assert.NoError(t, err)
 | |
| 			assert.Equal(t, ver, tc.expect)
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestMaxVersion(t *testing.T) {
 | |
| 	tcs := []struct {
 | |
| 		a, b, expect *semver.Version
 | |
| 	}{
 | |
| 		{
 | |
| 			a:      nil,
 | |
| 			b:      nil,
 | |
| 			expect: nil,
 | |
| 		},
 | |
| 		{
 | |
| 			a:      &version.V3_5,
 | |
| 			b:      nil,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			a:      nil,
 | |
| 			b:      &version.V3_5,
 | |
| 			expect: &version.V3_5,
 | |
| 		},
 | |
| 		{
 | |
| 			a:      &version.V3_6,
 | |
| 			b:      &version.V3_5,
 | |
| 			expect: &version.V3_6,
 | |
| 		},
 | |
| 		{
 | |
| 			a:      &version.V3_5,
 | |
| 			b:      &version.V3_6,
 | |
| 			expect: &version.V3_6,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tc := range tcs {
 | |
| 		t.Run(fmt.Sprintf("%v %v %v", tc.a, tc.b, tc.expect), func(t *testing.T) {
 | |
| 			got := maxVersion(tc.a, tc.b)
 | |
| 			assert.Equal(t, got, tc.expect)
 | |
| 		})
 | |
| 	}
 | |
| }
 | 
