fix(unix): handle Statfs field types for disk space check (#646)
* fix(unix): handle Statfs field types for disk space check Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix(freebsd): build disk space check without type mismatch Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --------- Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
25
cmd/bd/daemon_health_freebsd.go
Normal file
25
cmd/bd/daemon_health_freebsd.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//go:build freebsd && !wasm
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkDiskSpace returns the available disk space in MB for the given path.
|
||||||
|
// Returns (availableMB, true) on success, (0, false) on failure.
|
||||||
|
func checkDiskSpace(path string) (uint64, bool) {
|
||||||
|
var stat unix.Statfs_t
|
||||||
|
if err := unix.Statfs(path, &stat); err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat.Bavail < 0 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
|
||||||
|
availableBytes := uint64(stat.Bavail) * stat.Bsize //nolint:gosec
|
||||||
|
availableMB := availableBytes / (1024 * 1024)
|
||||||
|
|
||||||
|
return availableMB, true
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !windows && !wasm
|
//go:build !windows && !wasm && !freebsd
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -14,8 +14,8 @@ func checkDiskSpace(path string) (uint64, bool) {
|
|||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate available space in bytes, then convert to MB
|
// Calculate available space in bytes, then convert to MB.
|
||||||
// Bavail is uint64, Bsize is int64; overflow is intentional/safe in this context
|
// On most unix platforms, Bavail is unsigned but Bsize is signed.
|
||||||
availableBytes := stat.Bavail * uint64(stat.Bsize) //nolint:gosec
|
availableBytes := stat.Bavail * uint64(stat.Bsize) //nolint:gosec
|
||||||
availableMB := availableBytes / (1024 * 1024)
|
availableMB := availableBytes / (1024 * 1024)
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func TestConnectionLimits(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer store.Close()
|
defer store.Close()
|
||||||
|
|
||||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
socketPath := newTestSocketPath(t)
|
||||||
|
|
||||||
// Set low connection limit for testing
|
// Set low connection limit for testing
|
||||||
os.Setenv("BEADS_DAEMON_MAX_CONNS", "5")
|
os.Setenv("BEADS_DAEMON_MAX_CONNS", "5")
|
||||||
@@ -158,7 +158,7 @@ func TestRequestTimeout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer store.Close()
|
defer store.Close()
|
||||||
|
|
||||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
socketPath := newTestSocketPath(t)
|
||||||
|
|
||||||
// Set very short timeout for testing
|
// Set very short timeout for testing
|
||||||
os.Setenv("BEADS_DAEMON_REQUEST_TIMEOUT", "100ms")
|
os.Setenv("BEADS_DAEMON_REQUEST_TIMEOUT", "100ms")
|
||||||
@@ -199,14 +199,9 @@ func TestRequestTimeout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHealthResponseIncludesLimits(t *testing.T) {
|
func TestHealthResponseIncludesLimits(t *testing.T) {
|
||||||
tmpDir, err := os.MkdirTemp("", "bd-limits-test-*")
|
tmpDir := t.TempDir()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpDir)
|
|
||||||
|
|
||||||
dbPath := filepath.Join(tmpDir, "test.db")
|
dbPath := filepath.Join(tmpDir, "test.db")
|
||||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
socketPath := newTestSocketPath(t)
|
||||||
|
|
||||||
store, err := sqlite.New(context.Background(), dbPath)
|
store, err := sqlite.New(context.Background(), dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
func TestStatusEndpoint(t *testing.T) {
|
func TestStatusEndpoint(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
dbPath := filepath.Join(tmpDir, "test.db")
|
dbPath := filepath.Join(tmpDir, "test.db")
|
||||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
socketPath := newTestSocketPath(t)
|
||||||
|
|
||||||
store, err := sqlite.New(context.Background(), dbPath)
|
store, err := sqlite.New(context.Background(), dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -87,8 +87,7 @@ func TestStatusEndpoint(t *testing.T) {
|
|||||||
func TestStatusEndpointWithConfig(t *testing.T) {
|
func TestStatusEndpointWithConfig(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
dbPath := filepath.Join(tmpDir, "test.db")
|
dbPath := filepath.Join(tmpDir, "test.db")
|
||||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
socketPath := newTestSocketPath(t)
|
||||||
|
|
||||||
store, err := sqlite.New(context.Background(), dbPath)
|
store, err := sqlite.New(context.Background(), dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create storage: %v", err)
|
t.Fatalf("failed to create storage: %v", err)
|
||||||
@@ -146,8 +145,7 @@ func TestStatusEndpointWithConfig(t *testing.T) {
|
|||||||
func TestStatusEndpointLocalMode(t *testing.T) {
|
func TestStatusEndpointLocalMode(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
dbPath := filepath.Join(tmpDir, "test.db")
|
dbPath := filepath.Join(tmpDir, "test.db")
|
||||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
socketPath := newTestSocketPath(t)
|
||||||
|
|
||||||
store, err := sqlite.New(context.Background(), dbPath)
|
store, err := sqlite.New(context.Background(), dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create storage: %v", err)
|
t.Fatalf("failed to create storage: %v", err)
|
||||||
@@ -205,7 +203,7 @@ func TestStatusEndpointLocalMode(t *testing.T) {
|
|||||||
func TestStatusEndpointDefaultConfig(t *testing.T) {
|
func TestStatusEndpointDefaultConfig(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
dbPath := filepath.Join(tmpDir, "test.db")
|
dbPath := filepath.Join(tmpDir, "test.db")
|
||||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
socketPath := newTestSocketPath(t)
|
||||||
|
|
||||||
store, err := sqlite.New(context.Background(), dbPath)
|
store, err := sqlite.New(context.Background(), dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -262,7 +260,7 @@ func TestStatusEndpointDefaultConfig(t *testing.T) {
|
|||||||
func TestSetConfigConcurrency(t *testing.T) {
|
func TestSetConfigConcurrency(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
dbPath := filepath.Join(tmpDir, "test.db")
|
dbPath := filepath.Join(tmpDir, "test.db")
|
||||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
socketPath := newTestSocketPath(t)
|
||||||
|
|
||||||
store, err := sqlite.New(context.Background(), dbPath)
|
store, err := sqlite.New(context.Background(), dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package rpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||||
@@ -23,6 +26,22 @@ func newTestStore(t *testing.T, dbPath string) *sqlite.SQLiteStorage {
|
|||||||
_ = store.Close()
|
_ = store.Close()
|
||||||
t.Fatalf("Failed to set issue_prefix: %v", err)
|
t.Fatalf("Failed to set issue_prefix: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTestSocketPath(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// On unix, AF_UNIX socket paths have small length limits (notably on darwin).
|
||||||
|
// Prefer a short base dir when available.
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
d, err := os.MkdirTemp("/tmp", "beads-sock-")
|
||||||
|
if err == nil {
|
||||||
|
t.Cleanup(func() { _ = os.RemoveAll(d) })
|
||||||
|
return filepath.Join(d, "rpc.sock")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(t.TempDir(), "rpc.sock")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user