package runner
import (
"context"
"os"
"os/exec"
"path/filepath"
)
// Per-task temp directories (#317). A task killed before `go test`
// removed its go-build directories left them in the host's /tmp, a
// tmpfs that filled until NATS refused streams. Each task now gets a
// directory of its own under the runner's TempRoot, handed to it as
// TMPDIR and GOTMPDIR, and the runner removes that directory when the
// task ends, after reclaim has ended whatever the task left running.
// sweepTemp empties the runner's own temp root, which holds only what a
// previous life of this runner left; it never looks outside it.
func (r *Runner) sweepTemp() error {
root := r.cfg.TempRoot
mode := os.FileMode(0o700)
if r.users != nil {
// A job user must be able to reach its own directory under it.
mode = 0o711
}
if err := os.MkdirAll(root, mode); err != nil {
return err
}
if err := os.Chmod(root, mode); err != nil {
return err
}
es, err := os.ReadDir(root)
if err != nil {
return err
}
for _, e := range es {
if err := removeAll(filepath.Join(root, e.Name())); err != nil {
r.cfg.Logf("stale temp dir %s was not removed: %v", e.Name(), err)
}
}
return nil
}
// taskTemp makes one task's temp directory.
func (r *Runner) taskTemp() (string, error) {
if err := os.MkdirAll(r.cfg.TempRoot, 0o700); err != nil {
return "", err
}
return os.MkdirTemp(r.cfg.TempRoot, "task-")
}
// withTemp points every temp variable a command reads at tmp, after
// whatever the task itself set.
func withTemp(next starter, tmp string) starter {
temp := map[string]string{"TMPDIR": tmp, "GOTMPDIR": tmp, "TMP": tmp, "TEMP": tmp}
return func(ctx context.Context, dir string, argv []string, env ...map[string]string) *exec.Cmd {
return next(ctx, dir, argv, append(env, temp)...)
}
}
// removeAll removes dir, making read-only directories writable first
// where the plain removal is refused.
func removeAll(dir string) error {
err := os.RemoveAll(dir)
if err == nil {
return nil
}
filepath.WalkDir(dir, func(p string, d os.DirEntry, _ error) error {
if d != nil && d.IsDir() {
os.Chmod(p, 0o700)
}
return nil
})
return os.RemoveAll(dir)
}