Piotr Tabor 9e1abbab6e Fix goimports in all existing files. Execution of ./scripts/fix.sh
Signed-off-by: Piotr Tabor <ptab@google.com>
2022-12-29 09:41:31 +01:00

139 lines
4.6 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 schema
import (
"fmt"
"github.com/coreos/go-semver/semver"
"go.uber.org/zap"
"go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/server/v3/storage/backend"
)
// Validate checks provided backend to confirm that schema used is supported.
func Validate(lg *zap.Logger, tx backend.ReadTx) error {
tx.Lock()
defer tx.Unlock()
return unsafeValidate(lg, tx)
}
func unsafeValidate(lg *zap.Logger, tx backend.ReadTx) error {
current, err := UnsafeDetectSchemaVersion(lg, tx)
if err != nil {
// v3.5 requires a wal snapshot to persist its fields, so we can assign it a schema version.
lg.Warn("Failed to detect storage schema version. Please wait till wal snapshot before upgrading cluster.")
return nil
}
_, err = newPlan(lg, current, localBinaryVersion())
return err
}
func localBinaryVersion() semver.Version {
v := semver.New(version.Version)
return semver.Version{Major: v.Major, Minor: v.Minor}
}
type WALVersion interface {
// MinimalEtcdVersion returns minimal etcd version able to interpret WAL log.
MinimalEtcdVersion() *semver.Version
}
// Migrate updates storage schema to provided target version.
// Downgrading requires that provided WAL doesn't contain unsupported entries.
func Migrate(lg *zap.Logger, tx backend.BatchTx, w WALVersion, target semver.Version) error {
tx.LockOutsideApply()
defer tx.Unlock()
return UnsafeMigrate(lg, tx, w, target)
}
// UnsafeMigrate is non thread-safe version of Migrate.
func UnsafeMigrate(lg *zap.Logger, tx backend.BatchTx, w WALVersion, target semver.Version) error {
current, err := UnsafeDetectSchemaVersion(lg, tx)
if err != nil {
return fmt.Errorf("cannot detect storage schema version: %v", err)
}
plan, err := newPlan(lg, current, target)
if err != nil {
return fmt.Errorf("cannot create migration plan: %v", err)
}
if target.LessThan(current) {
minVersion := w.MinimalEtcdVersion()
if minVersion != nil && target.LessThan(*minVersion) {
return fmt.Errorf("cannot downgrade storage, WAL contains newer entries")
}
}
return plan.unsafeExecute(lg, tx)
}
// DetectSchemaVersion returns version of storage schema. Returned value depends on etcd version that created the backend. For
// * v3.6 and newer will return storage version.
// * v3.5 will return it's version if it includes all storage fields added in v3.5 (might require a snapshot).
// * v3.4 and older is not supported and will return error.
func DetectSchemaVersion(lg *zap.Logger, tx backend.ReadTx) (v semver.Version, err error) {
tx.RLock()
defer tx.RUnlock()
return UnsafeDetectSchemaVersion(lg, tx)
}
// UnsafeDetectSchemaVersion non-threadsafe version of DetectSchemaVersion.
func UnsafeDetectSchemaVersion(lg *zap.Logger, tx backend.ReadTx) (v semver.Version, err error) {
vp := UnsafeReadStorageVersion(tx)
if vp != nil {
return *vp, nil
}
confstate := UnsafeConfStateFromBackend(lg, tx)
if confstate == nil {
return v, fmt.Errorf("missing confstate information")
}
_, term := UnsafeReadConsistentIndex(tx)
if term == 0 {
return v, fmt.Errorf("missing term information")
}
return version.V3_5, nil
}
func schemaChangesForVersion(v semver.Version, isUpgrade bool) ([]schemaChange, error) {
// changes should be taken from higher version
var higherV = v
if isUpgrade {
higherV = semver.Version{Major: v.Major, Minor: v.Minor + 1}
}
actions, found := schemaChanges[higherV]
if !found {
if isUpgrade {
return nil, fmt.Errorf("version %q is not supported", higherV.String())
}
return nil, fmt.Errorf("version %q is not supported", v.String())
}
return actions, nil
}
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{
version.V3_6: {
addNewField(Meta, MetaStorageVersionName, emptyStorageVersion),
},
}
// 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("")
)