sweet/benchmark/internal/cgroups: test system-run functionality
Add a rudimentary test for starting a user scope with systemd-run. If it
doesn't work, then skip the wrapper.
For golang/go#54760.
Change-Id: I7b742b49ae453b8d485d7d0c008b9fb9148c37ac
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/426238
Run-TryBot: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
diff --git a/sweet/benchmarks/internal/cgroups/cgroups.go b/sweet/benchmarks/internal/cgroups/cgroups.go
index 878e344..0a55a3d 100644
--- a/sweet/benchmarks/internal/cgroups/cgroups.go
+++ b/sweet/benchmarks/internal/cgroups/cgroups.go
@@ -13,6 +13,54 @@
"path/filepath"
"strconv"
"strings"
+ "sync"
+ "time"
+)
+
+func findSystemdRun() (string, error) {
+ bin, err := exec.LookPath("systemd-run")
+ if errors.Is(err, exec.ErrNotFound) {
+ return "", fmt.Errorf("systemd-run binary not found")
+ } else if err != nil {
+ return "", fmt.Errorf("error looking for systemd-run: %w", err)
+ }
+
+ scope := fmt.Sprintf("systemd-run-test-%d.scope", time.Now().UnixNano())
+
+ cmd := exec.Command(bin, "--user", "--scope", "--unit", scope, "/bin/true")
+ sout, serr := cmd.CombinedOutput()
+ if serr == nil {
+ // It works!
+ return bin, nil
+ }
+
+ var context strings.Builder
+ fmt.Fprintf(&context, "\noutput: %s", string(sout))
+
+ // Failed. systemd-run probably just said to look at journalctl;
+ // collect that additional context.
+ cmd = exec.Command("journalctl", "--catalog", "--user", "--unit", scope)
+ jout, jerr := cmd.CombinedOutput()
+ if jerr != nil {
+ fmt.Fprintf(&context, "\njournalctl error: %v\noutout: %s", jerr, string(jout))
+ } else {
+ fmt.Fprintf(&context, "\njournalctl output: %s", string(jout))
+ }
+
+ // Attempt to cleanup unit.
+ cmd = exec.Command("systemctl", "--user", "reset-failed", scope)
+ scout, scerr := cmd.CombinedOutput()
+ if scerr != nil {
+ fmt.Fprintf(&context, "\nsystemctl cleanup error: %v\noutput: %s", scerr, string(scout))
+ }
+
+ return "", fmt.Errorf("system-run failed: %w%s", serr, context.String())
+}
+
+var (
+ systemdOnce sync.Once
+ systemdRunPath string
+ systemdRunError error
)
type Cmd struct {
@@ -25,13 +73,13 @@
func WrapCommand(cmd *exec.Cmd, scope string) (*Cmd, error) {
wrapped := Cmd{Cmd: *cmd}
- // TODO(mknyszek): Maybe make a more stringent check?
- systemdRunPath, err := exec.LookPath("systemd-run")
- if errors.Is(err, exec.ErrNotFound) {
- fmt.Fprintln(os.Stderr, "# warning: systemd-run not available, skipping...")
+ systemdOnce.Do(func() {
+ systemdRunPath, systemdRunError = findSystemdRun()
+ })
+
+ if systemdRunError != nil {
+ fmt.Fprintf(os.Stderr, "# warning: systemd-run not available: %v\n# skipping cgroup wrapper...\n", systemdRunError)
return &wrapped, nil
- } else if err != nil {
- return nil, err
}
u, err := user.Current()
@@ -62,3 +110,4 @@
return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
}
}
+