A full / made every check on shanks end 'store never became ready' with the reason erased by --rm. Builds now remove their local image tag, narrowed source and context clone; a build or check refuses under 20 GiB free on / and the workspace, saying so; the throwaway store is no longer started with --rm and a store that does not come up reports its exit code and last 20 log lines.
47 lines
1.4 KiB
Go
47 lines
1.4 KiB
Go
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
|
|
}
|