|
|
|
package snapshot
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/hashicorp/raft"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Upgrade writes a copy of the 7.x-format Snapshot dircectory at 'old' to a
|
|
|
|
// new Snapshot directory at 'new'. If the upgrade is successful, the
|
|
|
|
// 'old' directory is removed before the function returns.
|
|
|
|
func Upgrade(old, new string, logger *log.Logger) error {
|
|
|
|
newTmpDir := tmpName(new)
|
|
|
|
newGenerationDir := filepath.Join(newTmpDir, firstGeneration)
|
|
|
|
|
|
|
|
// If a temporary version of the new directory exists, remove it. This implies a
|
|
|
|
// previous upgrade attempt was interrupted. We will need to start over.
|
|
|
|
if dirExists(newTmpDir) {
|
|
|
|
if err := os.RemoveAll(newTmpDir); err != nil {
|
|
|
|
return fmt.Errorf("failed to remove temporary upgraded snapshot directory %s: %s", newTmpDir, err)
|
|
|
|
}
|
|
|
|
logger.Println("detected temporary upgraded snapshot directory, removing")
|
|
|
|
}
|
|
|
|
|
|
|
|
if dirExists(old) {
|
|
|
|
oldIsEmpty, err := dirIsEmpty(old)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to check if old snapshot directory %s is empty: %s", old, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if oldIsEmpty || dirExists(new) {
|
|
|
|
if err := os.RemoveAll(old); err != nil {
|
|
|
|
return fmt.Errorf("failed to remove old snapshot directory %s: %s", old, err)
|
|
|
|
}
|
|
|
|
logger.Printf("removed old snapshot directory %s as no upgrade is needed", old)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
logger.Printf("old snapshot directory %s does not exist, nothing to upgrade", old)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the upgrade process.
|
|
|
|
logger.Printf("upgrading snapshot directory %s to %s", old, new)
|
|
|
|
if err := os.MkdirAll(newTmpDir, 0755); err != nil {
|
|
|
|
return fmt.Errorf("failed to create temporary snapshot directory %s: %s", newTmpDir, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
oldMeta, err := getNewest7Snapshot(old)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to get newest snapshot from old snapshot directory %s: %s", old, err)
|
|
|
|
}
|
|
|
|
if oldMeta == nil {
|
|
|
|
// No snapshot to upgrade, this shouldn't happen since we checked for an empty old
|
|
|
|
// directory earlier.
|
|
|
|
return fmt.Errorf("no snapshot to upgrade in old snapshot directory %s", old)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write out the new meta file.
|
|
|
|
newSnapshotPath := filepath.Join(newGenerationDir, oldMeta.ID)
|
|
|
|
if err := os.MkdirAll(newSnapshotPath, 0755); err != nil {
|
|
|
|
return fmt.Errorf("failed to create new snapshot directory %s: %s", newSnapshotPath, err)
|
|
|
|
}
|
|
|
|
newMeta := &Meta{
|
|
|
|
SnapshotMeta: *oldMeta,
|
|
|
|
Full: true,
|
|
|
|
}
|
|
|
|
if err := writeMeta(newSnapshotPath, newMeta); err != nil {
|
|
|
|
return fmt.Errorf("failed to write new snapshot meta file: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write SQLite data into generation directory, as the base SQLite file.
|
|
|
|
newSqliteBasePath := filepath.Join(newGenerationDir, baseSqliteFile)
|
|
|
|
_ = newSqliteBasePath
|
|
|
|
|
|
|
|
// Read and decompress SQLite data into newSqliteBasePath
|
|
|
|
// Perform basic checks of data: is it a valid SQLite file? Run a PRAGMA integrity_check?
|
|
|
|
|
|
|
|
// Move the upgraded snapshot directory into place.
|
|
|
|
if err := os.Rename(newTmpDir, new); err != nil {
|
|
|
|
return fmt.Errorf("failed to move temporary snapshot directory %s to %s: %s", newTmpDir, new, err)
|
|
|
|
}
|
|
|
|
if err := syncDirParentMaybe(new); err != nil {
|
|
|
|
return fmt.Errorf("failed to sync parent directory of new snapshot directory %s: %s", new, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We're done! Remove old.
|
|
|
|
if err := removeDirSync(old); err != nil {
|
|
|
|
return fmt.Errorf("failed to remove old snapshot directory %s: %s", old, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// getNewest7Snapshot returns the newest snapshot Raft meta in the given directory.
|
|
|
|
func getNewest7Snapshot(dir string) (*raft.SnapshotMeta, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func dirIsEmpty(dir string) (bool, error) {
|
|
|
|
files, err := os.ReadDir(dir)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return len(files) == 0, nil
|
|
|
|
}
|