Files
soft_quay/core/installer/layout.go
T

119 lines
3.2 KiB
Go
Raw Normal View History

2026-07-16 16:32:58 +08:00
package installer
import (
"errors"
"fmt"
"os"
"path/filepath"
)
var (
ErrUnsafeInstallLayout = errors.New("unsafe install directory layout")
ErrStagingMissing = errors.New("staging directory is missing")
ErrBackupExists = errors.New("backup directory already exists")
)
type appLayout struct {
root string
current string
staging string
backup string
transaction string
transactionBackup string
}
type directoryState struct {
current bool
staging bool
backup bool
}
func inspectAppLayout(root string) (appLayout, error) {
if root == "" {
return appLayout{}, fmt.Errorf("%w: empty root", ErrUnsafeInstallLayout)
}
absolute, err := filepath.Abs(root)
if err != nil {
return appLayout{}, fmt.Errorf("%w: %v", ErrUnsafeInstallLayout, err)
}
info, err := os.Lstat(absolute)
if err != nil {
return appLayout{}, fmt.Errorf("%w: root: %v", ErrUnsafeInstallLayout, err)
}
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return appLayout{}, fmt.Errorf("%w: root is not a real directory", ErrUnsafeInstallLayout)
}
transaction, transactionBackup := transactionPaths(absolute)
return appLayout{
root: absolute,
current: filepath.Join(absolute, "current"),
staging: filepath.Join(absolute, "staging"),
backup: filepath.Join(absolute, "backup"),
transaction: transaction,
transactionBackup: transactionBackup,
}, nil
}
func inspectDirectories(layout appLayout) (directoryState, error) {
current, err := inspectManagedDirectory(layout.current)
if err != nil {
return directoryState{}, err
}
staging, err := inspectManagedDirectory(layout.staging)
if err != nil {
return directoryState{}, err
}
backup, err := inspectManagedDirectory(layout.backup)
if err != nil {
return directoryState{}, err
}
return directoryState{
current: current,
staging: staging,
backup: backup,
}, nil
}
func inspectManagedDirectory(path string) (bool, error) {
info, err := os.Lstat(path)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, fmt.Errorf("%w: inspect %s: %v", ErrUnsafeInstallLayout, path, err)
}
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return false, fmt.Errorf("%w: %s is not a real directory", ErrUnsafeInstallLayout, path)
}
return true, nil
}
func removeManagedDirectory(layout appLayout, target string) error {
return removeManagedDirectoryWithFence(layout, target, defaultDurability())
}
func removeManagedDirectoryWithFence(
layout appLayout,
target string,
fence durabilityFence,
) error {
2026-07-16 16:32:58 +08:00
if filepath.Dir(target) != layout.root {
return fmt.Errorf("%w: refuse removal outside app root", ErrUnsafeInstallLayout)
}
base := filepath.Base(target)
if base != "current" && base != "staging" && base != "backup" {
return fmt.Errorf("%w: refuse removal of %s", ErrUnsafeInstallLayout, base)
}
exists, err := inspectManagedDirectory(target)
if err != nil {
return err
}
if !exists {
return nil
}
if err := os.RemoveAll(target); err != nil {
return fmt.Errorf("remove managed directory %s: %w", base, err)
}
return syncDirectoryWithFence(fence, layout.root, "app root after directory removal")
2026-07-16 16:32:58 +08:00
}