100 lines
2.7 KiB
Go
100 lines
2.7 KiB
Go
package local_storage
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"git.blender.org/flamenco/pkg/crosspath"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type StorageInfo struct {
|
|
rootPath string
|
|
}
|
|
|
|
// NewNextToExe returns a storage representation that sits next to the
|
|
// currently-running executable. If that directory cannot be determined, falls
|
|
// back to the current working directory.
|
|
func NewNextToExe(subdir string) StorageInfo {
|
|
exeDir := getSuitableStorageRoot()
|
|
storagePath := filepath.Join(exeDir, subdir)
|
|
|
|
return StorageInfo{
|
|
rootPath: storagePath,
|
|
}
|
|
}
|
|
|
|
// Root returns the root path of the storage.
|
|
func (si StorageInfo) Root() string {
|
|
return si.rootPath
|
|
}
|
|
|
|
// ForJob returns the absolute directory path for storing job-related files.
|
|
func (si StorageInfo) ForJob(jobUUID string) string {
|
|
return filepath.Join(si.rootPath, relPathForJob(jobUUID))
|
|
}
|
|
|
|
// Erase removes the entire storage directory from disk.
|
|
func (si StorageInfo) Erase() error {
|
|
// A few safety measures before erasing the planet.
|
|
if si.rootPath == "" {
|
|
return fmt.Errorf("%+v.Erase(): refusing to erase empty directory", si)
|
|
}
|
|
if crosspath.IsRoot(si.rootPath) {
|
|
return fmt.Errorf("%+v.Erase(): refusing to erase root directory", si)
|
|
}
|
|
if home, found := os.LookupEnv("HOME"); found && home == si.rootPath {
|
|
return fmt.Errorf("%+v.Erase(): refusing to erase home directory %s", si, home)
|
|
}
|
|
|
|
log.Debug().Str("path", si.rootPath).Msg("erasing storage directory")
|
|
return os.RemoveAll(si.rootPath)
|
|
}
|
|
|
|
// MustErase removes the entire storage directory from disk, and panics if it
|
|
// cannot do that. This is primarily aimed at cleaning up at the end of unit
|
|
// tests.
|
|
func (si StorageInfo) MustErase() {
|
|
err := si.Erase()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// RelPath tries to make the given path relative to the local storage root.
|
|
// Assumes `path` is already an absolute path.
|
|
func (si StorageInfo) RelPath(path string) (string, error) {
|
|
return filepath.Rel(si.rootPath, path)
|
|
}
|
|
|
|
// Returns a sub-directory suitable for files of this job.
|
|
// Note that this is intentionally in sync with the `filepath()` function in
|
|
// `internal/manager/task_logs/task_logs.go`.
|
|
func relPathForJob(jobUUID string) string {
|
|
if jobUUID == "" {
|
|
return "jobless"
|
|
}
|
|
return filepath.Join("job-"+jobUUID[:4], jobUUID)
|
|
}
|
|
|
|
func getSuitableStorageRoot() string {
|
|
exename, err := os.Executable()
|
|
if err == nil {
|
|
return filepath.Dir(exename)
|
|
}
|
|
log.Error().Err(err).Msg("unable to determine the path of the currently running executable")
|
|
|
|
// Fall back to current working directory.
|
|
cwd, err := os.Getwd()
|
|
if err == nil {
|
|
return cwd
|
|
}
|
|
log.Error().Err(err).Msg("unable to determine the current working directory")
|
|
|
|
// Fall back to "." if all else fails.
|
|
return "."
|
|
}
|