feat: integrate new agentexec pkg (#15609)

- Integrates the `agentexec` pkg into the agent and removes the
legacy system of iterating over the process tree. It adds some linting
rules to hopefully catch future improper uses of `exec.Command` in the package.
This commit is contained in:
Jon Ayers
2024-11-27 20:12:15 +02:00
committed by GitHub
parent 45d9274aca
commit 1f238fed59
22 changed files with 147 additions and 1176 deletions
+14 -3
View File
@@ -23,7 +23,8 @@ const unset = -2000
// CLI runs the agent-exec command. It should only be called by the cli package.
func CLI() error {
// We lock the OS thread here to avoid a race condition where the nice priority
// we get is on a different thread from the one we set it on.
// we set gets applied to a different thread than the one we exec the provided
// command on.
runtime.LockOSThread()
// Nop on success but we do it anyway in case of an error.
defer runtime.UnlockOSThread()
@@ -68,12 +69,18 @@ func CLI() error {
err = unix.Setpriority(unix.PRIO_PROCESS, 0, *nice)
if err != nil {
return xerrors.Errorf("set nice score: %w", err)
// We alert the user instead of failing the command since it can be difficult to debug
// for a template admin otherwise. It's quite possible (and easy) to set an
// inappriopriate value for niceness.
printfStdErr("failed to adjust niceness to %q: %v", *nice, err)
}
err = writeOOMScoreAdj(*oom)
if err != nil {
return xerrors.Errorf("set oom score: %w", err)
// We alert the user instead of failing the command since it can be difficult to debug
// for a template admin otherwise. It's quite possible (and easy) to set an
// inappriopriate value for oom_score_adj.
printfStdErr("failed to adjust oom score to %q: %v", *nice, err)
}
path, err := exec.LookPath(args[0])
@@ -143,3 +150,7 @@ func execArgs(args []string) []string {
}
return nil
}
func printfStdErr(format string, a ...any) {
_, _ = fmt.Fprintf(os.Stderr, "coder-agent: %s\n", fmt.Sprintf(format, a...))
}
+26 -4
View File
@@ -10,6 +10,8 @@ import (
"strconv"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/pty"
)
const (
@@ -25,19 +27,39 @@ const (
// is returned. All instances of exec.Cmd should flow through this function to ensure
// proper resource constraints are applied to the child process.
func CommandContext(ctx context.Context, cmd string, args ...string) (*exec.Cmd, error) {
cmd, args, err := agentExecCmd(cmd, args...)
if err != nil {
return nil, xerrors.Errorf("agent exec cmd: %w", err)
}
return exec.CommandContext(ctx, cmd, args...), nil
}
// PTYCommandContext returns an pty.Cmd that calls "coder agent-exec" prior to exec'ing
// the provided command if CODER_PROC_PRIO_MGMT is set, otherwise a normal pty.Cmd
// is returned. All instances of pty.Cmd should flow through this function to ensure
// proper resource constraints are applied to the child process.
func PTYCommandContext(ctx context.Context, cmd string, args ...string) (*pty.Cmd, error) {
cmd, args, err := agentExecCmd(cmd, args...)
if err != nil {
return nil, xerrors.Errorf("agent exec cmd: %w", err)
}
return pty.CommandContext(ctx, cmd, args...), nil
}
func agentExecCmd(cmd string, args ...string) (string, []string, error) {
_, enabled := os.LookupEnv(EnvProcPrioMgmt)
if runtime.GOOS != "linux" || !enabled {
return exec.CommandContext(ctx, cmd, args...), nil
return cmd, args, nil
}
executable, err := os.Executable()
if err != nil {
return nil, xerrors.Errorf("get executable: %w", err)
return "", nil, xerrors.Errorf("get executable: %w", err)
}
bin, err := filepath.EvalSymlinks(executable)
if err != nil {
return nil, xerrors.Errorf("eval symlinks: %w", err)
return "", nil, xerrors.Errorf("eval symlinks: %w", err)
}
execArgs := []string{"agent-exec"}
@@ -51,7 +73,7 @@ func CommandContext(ctx context.Context, cmd string, args ...string) (*exec.Cmd,
execArgs = append(execArgs, "--", cmd)
execArgs = append(execArgs, args...)
return exec.CommandContext(ctx, bin, execArgs...), nil
return bin, execArgs, nil
}
// envValInt searches for a key in a list of environment variables and parses it to an int.