package builder import ( "fmt" "os" "path/filepath" "syscall" ) // FreeSpaceFloor is the free space a build node must have, on / and on its workspace's filesystem, before it takes // a build or a check (novox/hq issue 1834): under it, a throwaway store cannot initialise its data directory and // dies at once, and the check ended "never became ready" instead of saying the disk was full. const FreeSpaceFloor = 20 << 30 // FreeBytes reads the bytes an unprivileged writer can still use on the filesystem holding path. A variable so a // test answers for a disk it does not have. var FreeBytes = func(path string) (uint64, error) { var s syscall.Statfs_t if err := syscall.Statfs(path, &s); err != nil { return 0, err } return s.Bavail * uint64(s.Bsize), nil } // RoomToWork refuses, saying how much is free, when any of the paths' filesystems is under FreeSpaceFloor. A path // that does not exist yet (a workspace not made) is read at its nearest folder that does. func RoomToWork(paths ...string) error { for _, p := range paths { at := p for { if _, err := os.Stat(at); err == nil || filepath.Dir(at) == at { break } at = filepath.Dir(at) } free, err := FreeBytes(at) if err != nil { return fmt.Errorf("the free space on %s cannot be read: %w", p, err) } if free < FreeSpaceFloor { return fmt.Errorf("the build node has %d GiB free on %s, under the floor of %d GiB", free>>30, p, FreeSpaceFloor>>30) } } return nil }