server: Integrate version validation logic into tests

This commit is contained in:
Marek Siarkowicz 2021-10-05 17:21:20 +02:00
parent 620832a4a5
commit f92b4f9a28
5 changed files with 27 additions and 22 deletions

View File

@ -273,7 +273,8 @@ func (c *RaftCluster) Recover(onSet func(*zap.Logger, *semver.Version)) {
if c.downgradeInfo != nil { if c.downgradeInfo != nil {
d = &serverversion.DowngradeInfo{Enabled: c.downgradeInfo.Enabled, TargetVersion: c.downgradeInfo.TargetVersion} d = &serverversion.DowngradeInfo{Enabled: c.downgradeInfo.Enabled, TargetVersion: c.downgradeInfo.TargetVersion}
} }
serverversion.MustDetectDowngrade(c.lg, c.version, d) sv := semver.Must(semver.NewVersion(version.Version))
serverversion.MustDetectDowngrade(c.lg, sv, c.version, d)
onSet(c.lg, c.version) onSet(c.lg, c.version)
for _, m := range c.members { for _, m := range c.members {
@ -541,7 +542,8 @@ func (c *RaftCluster) SetVersion(ver *semver.Version, onSet func(*zap.Logger, *s
} }
oldVer := c.version oldVer := c.version
c.version = ver c.version = ver
serverversion.MustDetectDowngrade(c.lg, c.version, c.downgradeInfo) sv := semver.Must(semver.NewVersion(version.Version))
serverversion.MustDetectDowngrade(c.lg, sv, c.version, c.downgradeInfo)
if c.v2store != nil { if c.v2store != nil {
mustSaveClusterVersionToStore(c.lg, c.v2store, ver) mustSaveClusterVersionToStore(c.lg, c.v2store, ver)
} }

View File

@ -34,47 +34,46 @@ func (d *DowngradeInfo) GetTargetVersion() *semver.Version {
// isValidDowngrade verifies whether the cluster can be downgraded from verFrom to verTo // isValidDowngrade verifies whether the cluster can be downgraded from verFrom to verTo
func isValidDowngrade(verFrom *semver.Version, verTo *semver.Version) bool { func isValidDowngrade(verFrom *semver.Version, verTo *semver.Version) bool {
return verTo.Equal(*AllowedDowngradeVersion(verFrom)) return verTo.Equal(*allowedDowngradeVersion(verFrom))
} }
// MustDetectDowngrade will detect unexpected downgrade when the local server is recovered. // MustDetectDowngrade will detect unexpected downgrade when the local server is recovered.
func MustDetectDowngrade(lg *zap.Logger, cv *semver.Version, d *DowngradeInfo) { func MustDetectDowngrade(lg *zap.Logger, sv, cv *semver.Version, d *DowngradeInfo) {
lv := semver.Must(semver.NewVersion(version.Version))
// only keep major.minor version for comparison against cluster version // only keep major.minor version for comparison against cluster version
lv = &semver.Version{Major: lv.Major, Minor: lv.Minor} sv = &semver.Version{Major: sv.Major, Minor: sv.Minor}
// if the cluster enables downgrade, check local version against downgrade target version. // if the cluster enables downgrade, check local version against downgrade target version.
if d != nil && d.Enabled && d.TargetVersion != "" { if d != nil && d.Enabled && d.TargetVersion != "" {
if lv.Equal(*d.GetTargetVersion()) { if sv.Equal(*d.GetTargetVersion()) {
if cv != nil { if cv != nil {
lg.Info( lg.Info(
"cluster is downgrading to target version", "cluster is downgrading to target version",
zap.String("target-cluster-version", d.TargetVersion), zap.String("target-cluster-version", d.TargetVersion),
zap.String("determined-cluster-version", version.Cluster(cv.String())), zap.String("determined-cluster-version", version.Cluster(cv.String())),
zap.String("current-server-version", version.Version), zap.String("current-server-version", sv.String()),
) )
} }
return return
} }
lg.Panic( lg.Panic(
"invalid downgrade; server version is not allowed to join when downgrade is enabled", "invalid downgrade; server version is not allowed to join when downgrade is enabled",
zap.String("current-server-version", version.Version), zap.String("current-server-version", sv.String()),
zap.String("target-cluster-version", d.TargetVersion), zap.String("target-cluster-version", d.TargetVersion),
) )
} }
// if the cluster disables downgrade, check local version against determined cluster version. // if the cluster disables downgrade, check local version against determined cluster version.
// the validation passes when local version is not less than cluster version // the validation passes when local version is not less than cluster version
if cv != nil && lv.LessThan(*cv) { if cv != nil && sv.LessThan(*cv) {
lg.Panic( lg.Panic(
"invalid downgrade; server version is lower than determined cluster version", "invalid downgrade; server version is lower than determined cluster version",
zap.String("current-server-version", version.Version), zap.String("current-server-version", sv.String()),
zap.String("determined-cluster-version", version.Cluster(cv.String())), zap.String("determined-cluster-version", version.Cluster(cv.String())),
) )
} }
} }
func AllowedDowngradeVersion(ver *semver.Version) *semver.Version { func allowedDowngradeVersion(ver *semver.Version) *semver.Version {
// Todo: handle the case that downgrading from higher major version(e.g. downgrade from v4.0 to v3.x) // Todo: handle the case that downgrading from higher major version(e.g. downgrade from v4.0 to v3.x)
return &semver.Version{Major: ver.Major, Minor: ver.Minor - 1} return &semver.Version{Major: ver.Major, Minor: ver.Minor - 1}
} }

View File

@ -110,7 +110,8 @@ func TestMustDetectDowngrade(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
lg := zaptest.NewLogger(t) lg := zaptest.NewLogger(t)
err := tryMustDetectDowngrade(lg, tt.clusterVersion, tt.downgrade) sv := semver.Must(semver.NewVersion(version.Version))
err := tryMustDetectDowngrade(lg, sv, tt.clusterVersion, tt.downgrade)
if tt.success != (err == nil) { if tt.success != (err == nil) {
t.Errorf("Unexpected status, got %q, wanted: %v", err, tt.success) t.Errorf("Unexpected status, got %q, wanted: %v", err, tt.success)
@ -123,11 +124,11 @@ func TestMustDetectDowngrade(t *testing.T) {
} }
} }
func tryMustDetectDowngrade(lg *zap.Logger, cv *semver.Version, d *DowngradeInfo) (err interface{}) { func tryMustDetectDowngrade(lg *zap.Logger, sv, cv *semver.Version, d *DowngradeInfo) (err interface{}) {
defer func() { defer func() {
err = recover() err = recover()
}() }()
MustDetectDowngrade(lg, cv, d) MustDetectDowngrade(lg, sv, cv, d)
return err return err
} }

View File

@ -44,7 +44,7 @@ func (m *Manager) DowngradeValidate(ctx context.Context, targetVersion *semver.V
return err return err
} }
cv := m.s.GetClusterVersion() cv := m.s.GetClusterVersion()
allowedTargetVersion := AllowedDowngradeVersion(cv) allowedTargetVersion := allowedDowngradeVersion(cv)
if !targetVersion.Equal(*allowedTargetVersion) { if !targetVersion.Equal(*allowedTargetVersion) {
return ErrInvalidDowngradeTargetVersion return ErrInvalidDowngradeTargetVersion
} }

View File

@ -64,6 +64,7 @@ func TestUpgradeThreeNodes(t *testing.T) {
func newCluster(lg *zap.Logger, memberCount int, ver semver.Version) *clusterMock { func newCluster(lg *zap.Logger, memberCount int, ver semver.Version) *clusterMock {
cluster := &clusterMock{ cluster := &clusterMock{
lg: lg,
clusterVersion: ver, clusterVersion: ver,
members: make([]*memberMock, 0, memberCount), members: make([]*memberMock, 0, memberCount),
} }
@ -99,13 +100,14 @@ func (c *clusterMock) StepMonitors() {
} }
type clusterMock struct { type clusterMock struct {
lg *zap.Logger
clusterVersion semver.Version clusterVersion semver.Version
downgradeInfo *DowngradeInfo downgradeInfo *DowngradeInfo
members []*memberMock members []*memberMock
} }
func (c *clusterMock) DowngradeEnable(ver semver.Version) { func (c *clusterMock) Version() *Manager {
c.downgradeInfo = &DowngradeInfo{TargetVersion: ver.String(), Enabled: true} return NewManager(c.lg, c.members[0])
} }
func (c *clusterMock) MembersVersions() map[string]*version.Versions { func (c *clusterMock) MembersVersions() map[string]*version.Versions {
@ -120,9 +122,7 @@ func (c *clusterMock) MembersVersions() map[string]*version.Versions {
} }
func (c *clusterMock) ReplaceMemberBinary(mid int, newServerVersion semver.Version) { func (c *clusterMock) ReplaceMemberBinary(mid int, newServerVersion semver.Version) {
if newServerVersion.LessThan(c.clusterVersion) { MustDetectDowngrade(c.lg, &c.members[mid].serverVersion, &c.clusterVersion, c.downgradeInfo)
panic("Members cannot join clusters with higher version")
}
c.members[mid].serverVersion = newServerVersion c.members[mid].serverVersion = newServerVersion
} }
@ -146,7 +146,10 @@ func (m *memberMock) LinearizableReadNotify(ctx context.Context) error {
} }
func (m *memberMock) DowngradeEnable(ctx context.Context, targetVersion *semver.Version) error { func (m *memberMock) DowngradeEnable(ctx context.Context, targetVersion *semver.Version) error {
m.cluster.downgradeInfo = nil m.cluster.downgradeInfo = &DowngradeInfo{
TargetVersion: targetVersion.String(),
Enabled: true,
}
return nil return nil
} }