diff --git a/server/storage/schema/changes.go b/server/storage/schema/changes.go index 6eb0b7512093..c067ea927e2c 100644 --- a/server/storage/schema/changes.go +++ b/server/storage/schema/changes.go @@ -21,17 +21,22 @@ type schemaChange interface { downgradeAction() action } -// addNewField represents adding new field when upgrading. Downgrade will remove the field. -func addNewField(bucket backend.Bucket, fieldName []byte, fieldValue []byte) schemaChange { +type NewField struct { + Bucket backend.Bucket + FieldName []byte + FieldValue []byte +} + +func (f *NewField) schemaChange() schemaChange { return simpleSchemaChange{ upgrade: setKeyAction{ - Bucket: bucket, - FieldName: fieldName, - FieldValue: fieldValue, + Bucket: f.Bucket, + FieldName: f.FieldName, + FieldValue: f.FieldValue, }, downgrade: deleteKeyAction{ - Bucket: bucket, - FieldName: fieldName, + Bucket: f.Bucket, + FieldName: f.FieldName, }, } } diff --git a/server/storage/schema/changes_test.go b/server/storage/schema/changes_test.go index 05b8d49cf442..1ad88ace9093 100644 --- a/server/storage/schema/changes_test.go +++ b/server/storage/schema/changes_test.go @@ -30,7 +30,7 @@ func TestUpgradeDowngrade(t *testing.T) { }{ { name: "addNewField empty", - change: addNewField(Meta, []byte("/test"), []byte("1")), + change: (&NewField{Meta, []byte("/test"), []byte("1")}).schemaChange(), expectStateAfterUpgrade: map[string]string{"/test": "1"}, }, } diff --git a/server/storage/schema/schema.go b/server/storage/schema/schema.go index eb4fd3a2a5bf..180d52832a1a 100644 --- a/server/storage/schema/schema.go +++ b/server/storage/schema/schema.go @@ -123,14 +123,37 @@ func schemaChangesForVersion(v semver.Version, isUpgrade bool) ([]schemaChange, return actions, nil } +func NewFieldsForVersion(v semver.Version) []NewField { + if newFields, found := newFieldsMapping[v]; found { + return newFields + } + return nil +} + +func newFieldMappingsToSchemaChanges(newFieldMap map[semver.Version][]NewField) map[semver.Version][]schemaChange { + schemaChangeMap := map[semver.Version][]schemaChange{} + for ver, newFields := range newFieldMap { + changes := []schemaChange{} + for _, f := range newFields { + changes = append(changes, f.schemaChange()) + } + schemaChangeMap[ver] = changes + } + return schemaChangeMap +} + var ( - // schemaChanges list changes that were introduced in a particular version. + // newFieldsMapping list new fields 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{ + newFieldsMapping = map[semver.Version][]NewField{ version.V3_6: { - addNewField(Meta, MetaStorageVersionName, emptyStorageVersion), + {Meta, MetaStorageVersionName, emptyStorageVersion}, }, } + // 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 = newFieldMappingsToSchemaChanges(newFieldsMapping) + // emptyStorageVersion is used for v3.6 Step for the first time, in all other version StoragetVersion should be set by migrator. // Adding a addNewField for StorageVersion we can reuse logic to remove it when downgrading to v3.5 emptyStorageVersion = []byte("") diff --git a/tests/framework/e2e/etcd_process.go b/tests/framework/e2e/etcd_process.go index 735cfba84537..30613eae697e 100644 --- a/tests/framework/e2e/etcd_process.go +++ b/tests/framework/e2e/etcd_process.go @@ -31,7 +31,6 @@ import ( "github.com/coreos/go-semver/semver" "go.uber.org/zap" - "go.etcd.io/etcd/api/v3/version" "go.etcd.io/etcd/client/pkg/v3/fileutil" "go.etcd.io/etcd/pkg/v3/expect" "go.etcd.io/etcd/pkg/v3/proxy" @@ -279,12 +278,17 @@ func (ep *EtcdServerProcess) VerifySchemaVersion(lg *zap.Logger) error { if currentEtcdVer.LessThan(ver) || ver.LessThan(prevEtcdVer) { return fmt.Errorf("expect backend schema version to be between [%s, %s], but got %s", prevEtcdVer.String(), currentEtcdVer.String(), ver.String()) } - // check new fields introduced in V3_6 do not exist in V3_5 data file. - // V3_6 contains all the fields in V3_5, so no need to check for V3_6 servers. - if *currentEtcdVer == version.V3_5 { - _, vs := be.BatchTx().UnsafeRange(schema.Meta, schema.MetaStorageVersionName, nil, 1) + // storage schema is generally backward compatible. No need to check the buckets for higher version. + if ep.cfg.ExecPath == BinPath.Etcd { + return nil + } + lg.Info("verify no new storage schema field is present in the db file of last release process") + nextEtcdVer := semver.Version{Major: currentEtcdVer.Major, Minor: currentEtcdVer.Minor + 1} + newFields := schema.NewFieldsForVersion(nextEtcdVer) + for _, f := range newFields { + _, vs := be.BatchTx().UnsafeRange(f.Bucket, f.FieldName, nil, 1) if len(vs) != 0 { - return fmt.Errorf("expect storageVersion not exist in the meta bucket, but got %s", string(vs[0])) + return fmt.Errorf("expect %s not exist in the %s bucket, but got %s", f.Bucket.Name(), f.FieldName, vs[0]) } } return nil