Fence installation transaction durability (T-613)
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package installer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
)
|
||||
|
||||
var ErrDurability = errors.New("install durability fence failed")
|
||||
|
||||
type durabilityFence interface {
|
||||
syncFile(file *os.File) error
|
||||
syncDirectory(path string) error
|
||||
}
|
||||
|
||||
type filesystemDurability struct{}
|
||||
|
||||
func (filesystemDurability) syncFile(file *os.File) error {
|
||||
return file.Sync()
|
||||
}
|
||||
|
||||
func (filesystemDurability) syncDirectory(path string) error {
|
||||
return syncDirectoryPath(path)
|
||||
}
|
||||
|
||||
func defaultDurability() durabilityFence {
|
||||
return filesystemDurability{}
|
||||
}
|
||||
|
||||
func effectiveDurability(fence durabilityFence) durabilityFence {
|
||||
if fence == nil {
|
||||
return defaultDurability()
|
||||
}
|
||||
return fence
|
||||
}
|
||||
|
||||
func syncFileWithFence(fence durabilityFence, file *os.File, description string) error {
|
||||
if err := effectiveDurability(fence).syncFile(file); err != nil {
|
||||
return fmt.Errorf("%w: sync %s: %v", ErrDurability, description, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func syncDirectoryWithFence(fence durabilityFence, path, description string) error {
|
||||
if err := effectiveDurability(fence).syncDirectory(path); err != nil {
|
||||
return fmt.Errorf("%w: sync %s: %v", ErrDurability, description, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func syncStagingTree(fence durabilityFence, root string) error {
|
||||
directories := make([]string, 0)
|
||||
err := filepath.WalkDir(root, func(path string, entry fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if entry.Type()&os.ModeSymlink != 0 {
|
||||
return fmt.Errorf("%w: staging tree contains a symbolic link", ErrUnsafeInstallLayout)
|
||||
}
|
||||
if entry.IsDir() {
|
||||
directories = append(directories, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: walk staging tree: %w", ErrDurability, err)
|
||||
}
|
||||
sort.Slice(directories, func(left, right int) bool {
|
||||
return len(directories[left]) > len(directories[right])
|
||||
})
|
||||
for _, directory := range directories {
|
||||
if err := syncDirectoryWithFence(fence, directory, "staging directory"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
parent := filepath.Dir(root)
|
||||
if parent != root {
|
||||
if err := syncDirectoryWithFence(fence, parent, "staging parent directory"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func renameManagedDirectory(
|
||||
layout appLayout,
|
||||
source string,
|
||||
target string,
|
||||
fence durabilityFence,
|
||||
description string,
|
||||
) error {
|
||||
if err := os.Rename(source, target); err != nil {
|
||||
return err
|
||||
}
|
||||
return syncDirectoryWithFence(fence, layout.root, description)
|
||||
}
|
||||
Reference in New Issue
Block a user