client io architecture

This commit is contained in:
Ilia Denisov
2026-03-12 18:45:46 +02:00
committed by GitHub
parent 2dafa69b93
commit 079b9facb0
36 changed files with 1810 additions and 460 deletions
+1 -1
View File
@@ -7,7 +7,6 @@ require (
github.com/go-playground/validator/v10 v10.30.1
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.11.1
golang.org/x/sys v0.41.0
)
require (
@@ -39,6 +38,7 @@ require (
golang.org/x/arch v0.24.0 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+9 -8
View File
@@ -4,6 +4,7 @@ import (
"encoding"
"errors"
"fmt"
"galaxy/util"
"math/big"
"os"
"path/filepath"
@@ -28,13 +29,13 @@ func NewFileStorage(path string) (*fs, error) {
if err != nil {
return nil, fmt.Errorf("path %s invalid: %s", path, err)
}
if ok, err := dirExists(absPath); err != nil {
if ok, err := util.DirExists(absPath); err != nil {
return nil, fmt.Errorf("check dir exist: %s", err)
} else if !ok {
return nil, errors.New("directory does not exist: " + absPath)
}
if ok, err := writable(absPath); err != nil {
if ok, err := util.Writable(absPath); err != nil {
return nil, fmt.Errorf("check dir access: %s", err)
} else if !ok {
return nil, errors.New("directory should have read-write access: " + absPath)
@@ -48,7 +49,7 @@ func NewFileStorage(path string) (*fs, error) {
func (f *fs) Lock() (func() error, error) {
lockPath := f.lockFilePath()
exists, err := fileExists(lockPath)
exists, err := util.FileExists(lockPath)
if err != nil {
return nil, fmt.Errorf("check lock file exists: %s", err)
}
@@ -77,7 +78,7 @@ func (f *fs) Lock() (func() error, error) {
}
func (f *fs) Exists(path string) (bool, error) {
return fileExists(filepath.Join(f.root, path))
return util.FileExists(filepath.Join(f.root, path))
}
func (f *fs) WriteSafe(path string, v encoding.BinaryMarshaler) error {
@@ -97,7 +98,7 @@ func (f *fs) WriteSafe(path string, v encoding.BinaryMarshaler) error {
targetDir := filepath.Dir(targetFilePath)
if targetDir != f.root {
ok, err := dirExists(targetDir)
ok, err := util.DirExists(targetDir)
if err != nil {
return fmt.Errorf("check target dir exists: %s", err)
}
@@ -110,12 +111,12 @@ func (f *fs) WriteSafe(path string, v encoding.BinaryMarshaler) error {
}
oldFilePath := targetFilePath + oldFileSuffix
targetExists, err := fileExists(targetFilePath)
targetExists, err := util.FileExists(targetFilePath)
if err != nil {
return fmt.Errorf("check target file exists: %s", err)
}
if targetExists {
oldFileExists, err := fileExists(oldFilePath)
oldFileExists, err := util.FileExists(oldFilePath)
if err != nil {
return fmt.Errorf("check old file exists: %s", err)
}
@@ -125,7 +126,7 @@ func (f *fs) WriteSafe(path string, v encoding.BinaryMarshaler) error {
}
newFilePath := targetFilePath + newFileSuffix
newFileExists, err := fileExists(newFilePath)
newFileExists, err := util.FileExists(newFilePath)
if err != nil {
return fmt.Errorf("check new file exists: %s", err)
}
+29 -10
View File
@@ -1,26 +1,45 @@
package fs
package fs_test
import (
"os"
"path/filepath"
"slices"
"testing"
"galaxy/server/internal/repo/fs"
"galaxy/util"
"github.com/stretchr/testify/assert"
)
const (
lockFile = ".lock"
)
type sampleData struct {
data []byte
}
func (sd *sampleData) UnmarshalBinary(data []byte) error {
sd.data = slices.Clone(data)
return nil
}
func (sd sampleData) MarshalBinary() (data []byte, err error) {
return sd.data, nil
}
func TestNewFileStorageSuccess(t *testing.T) {
root, cleanup := util.CreateWorkDir(t)
defer cleanup()
_, err := NewFileStorage(root)
_, err := fs.NewFileStorage(root)
assert.NoError(t, err)
}
func TestLock(t *testing.T) {
root, cleanup := util.CreateWorkDir(t)
defer cleanup()
fs, err := NewFileStorage(root)
fs, err := fs.NewFileStorage(root)
assert.NoError(t, err, "create file storage")
unlock, err := fs.Lock()
assert.NoError(t, err, "acquire lock")
@@ -40,7 +59,7 @@ func TestExist(t *testing.T) {
t.Fatal(err)
}
fs, err := NewFileStorage(root)
fs, err := fs.NewFileStorage(root)
assert.NoError(t, err, "create file storage")
exist, err := fs.Exists(fileName)
@@ -56,7 +75,7 @@ func TestWrite(t *testing.T) {
root, cleanup := util.CreateWorkDir(t)
defer cleanup()
fs, err := NewFileStorage(root)
fs, err := fs.NewFileStorage(root)
assert.NoError(t, err, "create file storage: %s", err)
unlock, err := fs.Lock()
@@ -99,7 +118,7 @@ func TestRead(t *testing.T) {
sd := new(sampleData)
fs, err := NewFileStorage(root)
fs, err := fs.NewFileStorage(root)
assert.NoError(t, err, "create file storage: %s", err)
assert.EqualError(t, fs.Read("some.file", sd), "lock must be acquired before read")
@@ -144,7 +163,7 @@ func TestRead(t *testing.T) {
func TestWriteErrorWithoutLock(t *testing.T) {
root, cleanup := util.CreateWorkDir(t)
defer cleanup()
fs, err := NewFileStorage(root)
fs, err := fs.NewFileStorage(root)
assert.NoError(t, err, "create file storage")
sd := &sampleData{[]byte{0, 1, 2, 3}}
err = fs.Write("some/path", sd)
@@ -153,7 +172,7 @@ func TestWriteErrorWithoutLock(t *testing.T) {
}
func TestNewFileStorageErrorNotExists(t *testing.T) {
_, err := NewFileStorage(filepath.Join(os.TempDir(), "non-existent-dir"))
_, err := fs.NewFileStorage(filepath.Join(os.TempDir(), "non-existent-dir"))
assert.Error(t, err)
}
@@ -165,7 +184,7 @@ func TestNewFileStorageErrorNotADirectory(t *testing.T) {
if err := f.Close(); err != nil {
t.Fatal(err)
}
_, err = NewFileStorage(f.Name())
_, err = fs.NewFileStorage(f.Name())
assert.Error(t, err)
if err := os.Remove(f.Name()); err != nil {
t.Fatal(err)
@@ -173,6 +192,6 @@ func TestNewFileStorageErrorNotADirectory(t *testing.T) {
}
func TestNewFileStorageErrorNoAccess(t *testing.T) {
_, err := NewFileStorage(nonWritableDir)
_, err := fs.NewFileStorage("/some/random/dir")
assert.Error(t, err)
}
-22
View File
@@ -1,22 +0,0 @@
package fs
import (
"slices"
)
const (
nonWritableDir = "/usr/lib"
)
type sampleData struct {
data []byte
}
func (sd *sampleData) UnmarshalBinary(data []byte) error {
sd.data = slices.Clone(data)
return nil
}
func (sd sampleData) MarshalBinary() (data []byte, err error) {
return sd.data, nil
}
-29
View File
@@ -1,29 +0,0 @@
// for windows builds func [writable] should be refactored
package fs
import (
"fmt"
"os"
)
func dirExists(path string) (bool, error) {
return pathExists(path, true)
}
func fileExists(path string) (bool, error) {
return pathExists(path, false)
}
func pathExists(path string, isDir bool) (bool, error) {
if fi, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
} else {
if isDir != fi.IsDir() {
return false, fmt.Errorf("wrong type: "+path+" mode=%s isDir=%t", fi.Mode(), isDir)
}
return true, nil
}
}
-79
View File
@@ -1,79 +0,0 @@
package fs
import (
"os"
"path/filepath"
"testing"
"galaxy/util"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestPathExists(t *testing.T) {
root, cleanup := util.CreateWorkDir(t)
defer cleanup()
testDirExistsFunc(t, root, func(s string) (bool, error) { return pathExists(s, true) })
testFileExistsFunc(t, root, func(s string) (bool, error) { return pathExists(s, false) })
}
func TestDirExists(t *testing.T) {
root, cleanup := util.CreateWorkDir(t)
defer cleanup()
testDirExistsFunc(t, root, dirExists)
}
func TestFileExists(t *testing.T) {
root, cleanup := util.CreateWorkDir(t)
defer cleanup()
testFileExistsFunc(t, root, fileExists)
}
func TestWritable(t *testing.T) {
root, cleanup := util.CreateWorkDir(t)
defer cleanup()
ok, err := writable(root)
assert.NoError(t, err, "directory writable check")
assert.True(t, ok, "directory should be writable")
ok, err = writable(nonWritableDir)
assert.NoError(t, err, "system directory writable check")
assert.False(t, ok, "system directory should not be writable")
}
func testDirExistsFunc(t *testing.T, root string, dirCheck func(string) (bool, error)) {
exists, err := dirCheck(root)
assert.NoError(t, err, "directory existence check")
assert.True(t, exists, "directory should exist")
nonExistentDir := filepath.Join(root, uuid.New().String())
exists, err = dirCheck(nonExistentDir)
assert.NoError(t, err, "non-existent directory existence check")
assert.False(t, exists, "non-existent directory should not exist")
}
func testFileExistsFunc(t *testing.T, root string, fileCheck func(string) (bool, error)) {
fpath := createTempFile(t, root)
exists, err := fileCheck(fpath)
assert.NoError(t, err, "file existence check")
assert.True(t, exists, "file should exist")
nonExistentFile := filepath.Join(root, uuid.New().String())
exists, err = fileCheck(nonExistentFile)
assert.NoError(t, err, "non-existent file existence check")
assert.False(t, exists, "non-existent file should not exist")
}
func createTempFile(t *testing.T, root string) string {
t.Helper()
fd, err := os.CreateTemp(root, "a-file")
if err != nil {
assert.FailNow(t, "create temporary file", err)
return ""
}
if err := fd.Close(); err != nil {
assert.FailNow(t, "close temporary file", err)
return ""
}
return fd.Name()
}
-15
View File
@@ -1,15 +0,0 @@
//go:build unix || (js && wasm) || wasip1
package fs
import "golang.org/x/sys/unix"
// writable reports whether path is writable on Windows.
//
// Semantics:
// - for an existing regular file, it tries to open it for writing;
// - for an existing directory, it tries to create and remove a temp file inside it;
// - for other file types, it returns false with no error.
func writable(filepath string) (bool, error) {
return unix.Access(filepath, unix.W_OK) == nil, nil
}
-94
View File
@@ -1,94 +0,0 @@
package fs
import (
"errors"
"os"
"path/filepath"
"syscall"
)
// writable reports whether path is writable on Windows.
//
// Semantics:
// - for an existing regular file, it tries to open it for writing;
// - for an existing directory, it tries to create and remove a temp file inside it;
// - for other file types, it returns false with no error.
//
// This is intentionally an operational check, not a mode-bit check, because
// on Windows effective writability is determined by ACLs and file attributes,
// not by POSIX-like permission bits from os.FileMode.
func writable(path string) (bool, error) {
info, err := os.Stat(path)
if err != nil {
return false, err
}
if info.IsDir() {
return writableDir(path)
}
if !info.Mode().IsRegular() {
return false, nil
}
return writableFile(path)
}
// writableFile checks whether an existing regular file can be opened for writing.
func writableFile(path string) (bool, error) {
f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0)
if err == nil {
_ = f.Close()
return true, nil
}
if isPermissionLikeError(err) {
return false, nil
}
return false, err
}
// writableDir checks whether a directory allows creating a child file.
// That is usually the most useful definition of "directory is writable".
func writableDir(path string) (bool, error) {
pattern := filepath.Join(path, ".writable-check-*")
f, err := os.CreateTemp(path, filepath.Base(pattern))
if err == nil {
name := f.Name()
_ = f.Close()
_ = os.Remove(name)
return true, nil
}
if isPermissionLikeError(err) {
return false, nil
}
return false, err
}
// isPermissionLikeError normalizes the common Windows "access denied" style
// failures to a simple false result instead of surfacing them as hard errors.
func isPermissionLikeError(err error) bool {
if err == nil {
return false
}
if errors.Is(err, os.ErrPermission) {
return true
}
var errno syscall.Errno
if errors.As(err, &errno) {
// ERROR_ACCESS_DENIED
if errno == syscall.ERROR_ACCESS_DENIED {
return true
}
}
var pathErr *os.PathError
if errors.As(err, &pathErr) && errors.Is(pathErr.Err, os.ErrPermission) {
return true
}
return false
}
@@ -1,50 +0,0 @@
//go:build windows
package fs
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
// TestWritable_NewFile verifies that a freshly created regular file is writable.
func TestWritable_NewFile(t *testing.T) {
t.Parallel()
dir := t.TempDir()
path := filepath.Join(dir, "file.txt")
err := os.WriteFile(path, []byte("x"), 0o600)
require.NoError(t, err)
ok, err := writable(path)
require.NoError(t, err)
require.True(t, ok)
}
// TestWritable_NewDirectory verifies that a freshly created directory is writable
// by checking that a temp file can be created inside it.
func TestWritable_NewDirectory(t *testing.T) {
t.Parallel()
dir := t.TempDir()
ok, err := writable(dir)
require.NoError(t, err)
require.True(t, ok)
}
// TestWritable_MissingPath verifies that a missing path returns an error from Stat.
func TestWritable_MissingPath(t *testing.T) {
t.Parallel()
dir := t.TempDir()
path := filepath.Join(dir, "missing")
ok, err := writable(path)
require.Error(t, err)
require.False(t, ok)
}