@@ -20,60 +20,101 @@ const (
2020 EnvProcPrioMgmt = "CODER_PROC_PRIO_MGMT"
2121 EnvProcOOMScore = "CODER_PROC_OOM_SCORE"
2222 EnvProcNiceScore = "CODER_PROC_NICE_SCORE"
23- )
2423
25- // CommandContext returns an exec.Cmd that calls "coder agent-exec" prior to exec'ing
26- // the provided command if CODER_PROC_PRIO_MGMT is set, otherwise a normal exec.Cmd
27- // is returned. All instances of exec.Cmd should flow through this function to ensure
28- // proper resource constraints are applied to the child process.
29- func CommandContext (ctx context.Context , cmd string , args ... string ) (* exec.Cmd , error ) {
30- cmd , args , err := agentExecCmd (cmd , args ... )
31- if err != nil {
32- return nil , xerrors .Errorf ("agent exec cmd: %w" , err )
33- }
34- return exec .CommandContext (ctx , cmd , args ... ), nil
35- }
24+ // unset is set to an invalid value for nice and oom scores.
25+ unset = - 2000
26+ )
3627
37- // PTYCommandContext returns an pty.Cmd that calls "coder agent-exec" prior to exec'ing
38- // the provided command if CODER_PROC_PRIO_MGMT is set, otherwise a normal pty.Cmd
39- // is returned. All instances of pty.Cmd should flow through this function to ensure
40- // proper resource constraints are applied to the child process.
41- func PTYCommandContext (ctx context.Context , cmd string , args ... string ) (* pty.Cmd , error ) {
42- cmd , args , err := agentExecCmd (cmd , args ... )
43- if err != nil {
44- return nil , xerrors .Errorf ("agent exec cmd: %w" , err )
45- }
46- return pty .CommandContext (ctx , cmd , args ... ), nil
28+ var DefaultExecer Execer = execer {}
29+
30+ // Execer defines an abstraction for creating exec.Cmd variants. It's unfortunately
31+ // necessary because we need to be able to wrap child processes with "coder agent-exec"
32+ // for templates that expect the agent to manage process priority.
33+ type Execer interface {
34+ // CommandContext returns an exec.Cmd that calls "coder agent-exec" prior to exec'ing
35+ // the provided command if CODER_PROC_PRIO_MGMT is set, otherwise a normal exec.Cmd
36+ // is returned. All instances of exec.Cmd should flow through this function to ensure
37+ // proper resource constraints are applied to the child process.
38+ CommandContext (ctx context.Context , cmd string , args ... string ) * exec.Cmd
39+ // PTYCommandContext returns an pty.Cmd that calls "coder agent-exec" prior to exec'ing
40+ // the provided command if CODER_PROC_PRIO_MGMT is set, otherwise a normal pty.Cmd
41+ // is returned. All instances of pty.Cmd should flow through this function to ensure
42+ // proper resource constraints are applied to the child process.
43+ PTYCommandContext (ctx context.Context , cmd string , args ... string ) * pty.Cmd
4744}
4845
49- func agentExecCmd ( cmd string , args ... string ) (string , [] string , error ) {
46+ func NewExecer ( ) (Execer , error ) {
5047 _ , enabled := os .LookupEnv (EnvProcPrioMgmt )
5148 if runtime .GOOS != "linux" || ! enabled {
52- return cmd , args , nil
49+ return DefaultExecer , nil
5350 }
5451
5552 executable , err := os .Executable ()
5653 if err != nil {
57- return "" , nil , xerrors .Errorf ("get executable: %w" , err )
54+ return nil , xerrors .Errorf ("get executable: %w" , err )
5855 }
5956
6057 bin , err := filepath .EvalSymlinks (executable )
6158 if err != nil {
62- return "" , nil , xerrors .Errorf ("eval symlinks: %w" , err )
59+ return nil , xerrors .Errorf ("eval symlinks: %w" , err )
60+ }
61+
62+ oomScore , ok := envValInt (EnvProcOOMScore )
63+ if ! ok {
64+ oomScore = unset
65+ }
66+
67+ niceScore , ok := envValInt (EnvProcNiceScore )
68+ if ! ok {
69+ niceScore = unset
6370 }
6471
72+ return priorityExecer {
73+ binPath : bin ,
74+ oomScore : oomScore ,
75+ niceScore : niceScore ,
76+ }, nil
77+ }
78+
79+ type execer struct {}
80+
81+ func (execer ) CommandContext (ctx context.Context , cmd string , args ... string ) * exec.Cmd {
82+ return exec .CommandContext (ctx , cmd , args ... )
83+ }
84+
85+ func (execer ) PTYCommandContext (ctx context.Context , cmd string , args ... string ) * pty.Cmd {
86+ return pty .CommandContext (ctx , cmd , args ... )
87+ }
88+
89+ type priorityExecer struct {
90+ binPath string
91+ oomScore int
92+ niceScore int
93+ }
94+
95+ func (e priorityExecer ) CommandContext (ctx context.Context , cmd string , args ... string ) * exec.Cmd {
96+ cmd , args = e .agentExecCmd (cmd , args ... )
97+ return exec .CommandContext (ctx , cmd , args ... )
98+ }
99+
100+ func (e priorityExecer ) PTYCommandContext (ctx context.Context , cmd string , args ... string ) * pty.Cmd {
101+ cmd , args = e .agentExecCmd (cmd , args ... )
102+ return pty .CommandContext (ctx , cmd , args ... )
103+ }
104+
105+ func (e priorityExecer ) agentExecCmd (cmd string , args ... string ) (string , []string ) {
65106 execArgs := []string {"agent-exec" }
66- if score , ok := envValInt ( EnvProcOOMScore ); ok {
67- execArgs = append (execArgs , oomScoreArg (score ))
107+ if e . oomScore != unset {
108+ execArgs = append (execArgs , oomScoreArg (e . oomScore ))
68109 }
69110
70- if score , ok := envValInt ( EnvProcNiceScore ); ok {
71- execArgs = append (execArgs , niceScoreArg (score ))
111+ if e . niceScore != unset {
112+ execArgs = append (execArgs , niceScoreArg (e . niceScore ))
72113 }
73114 execArgs = append (execArgs , "--" , cmd )
74115 execArgs = append (execArgs , args ... )
75116
76- return bin , execArgs , nil
117+ return e . binPath , execArgs
77118}
78119
79120// envValInt searches for a key in a list of environment variables and parses it to an int.
0 commit comments