Skip to content

feat: add agent exec pkg #15577

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Nov 25, 2024
Prev Previous commit
Next Next commit
add basic test
  • Loading branch information
sreya committed Nov 18, 2024
commit 00c9cd71721842df30f4c52015b54dd11b80336f
3 changes: 1 addition & 2 deletions agent/agentexec/cli.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package agentexec

import (
"context"
"fmt"
"os"
"os/exec"
Expand All @@ -20,7 +19,7 @@
)

// CLI runs the agent-exec command. It should only be called by the cli package.
func CLI(ctx context.Context, args []string, environ []string) error {
func CLI(args []string, environ []string) error {
if runtime.GOOS != "linux" {
return xerrors.Errorf("agent-exec is only supported on Linux")
}
Expand All @@ -42,7 +41,7 @@
return xerrors.Errorf("invalid nice score: %w", err)
}

err = syscall.Setpriority(syscall.PRIO_PROCESS, pid, score)

Check failure on line 44 in agent/agentexec/cli.go

View workflow job for this annotation

GitHub Actions / test-go (windows-2022)

undefined: syscall.Setpriority

Check failure on line 44 in agent/agentexec/cli.go

View workflow job for this annotation

GitHub Actions / test-go (windows-2022)

undefined: syscall.PRIO_PROCESS

Check failure on line 44 in agent/agentexec/cli.go

View workflow job for this annotation

GitHub Actions / test-go (windows-2022)

undefined: syscall.Setpriority

Check failure on line 44 in agent/agentexec/cli.go

View workflow job for this annotation

GitHub Actions / test-go (windows-2022)

undefined: syscall.PRIO_PROCESS
if err != nil {
return xerrors.Errorf("set nice score: %w", err)
}
Expand Down
90 changes: 90 additions & 0 deletions agent/agentexec/cli_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package agentexec_test

import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
"testing"

"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"

"github.com/coder/coder/v2/testutil"
)

func TestCLI(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
t.Parallel()

ctx := testutil.Context(t, testutil.WaitMedium)
cmd, dir := cmd(t, ctx)
cmd.Env = append(cmd.Env, "CODER_PROC_NICE_SCORE=10")
cmd.Env = append(cmd.Env, "CODER_PROC_OOM_SCORE=123")
err := cmd.Start()
require.NoError(t, err)

waitForSentinel(t, ctx, cmd, dir)
requireOOMScore(t, cmd.Process.Pid, 123)
requireNiceScore(t, cmd.Process.Pid, 10)
})
}

func requireNiceScore(t *testing.T, pid int, score int) {
t.Helper()

nice, err := unix.Getpriority(0, pid)

Check failure on line 41 in agent/agentexec/cli_test.go

View workflow job for this annotation

GitHub Actions / test-go (windows-2022)

undefined: unix.Getpriority

Check failure on line 41 in agent/agentexec/cli_test.go

View workflow job for this annotation

GitHub Actions / test-go (windows-2022)

undefined: unix.Getpriority
require.NoError(t, err)
require.Equal(t, score, nice)
}

func requireOOMScore(t *testing.T, pid int, expected int) {
t.Helper()

actual, err := os.ReadFile(fmt.Sprintf("/proc/%d/oom_score_adj", pid))
require.NoError(t, err)
score := strings.TrimSpace(string(actual))
require.Equal(t, strconv.Itoa(expected), score)
}

func waitForSentinel(t *testing.T, ctx context.Context, cmd *exec.Cmd, dir string) {
t.Helper()

require.Eventually(t, func() bool {
// Check if the process is still running.
err := cmd.Process.Signal(syscall.Signal(0))
require.NoError(t, err)

_, err = os.Stat(dir)
return err == nil && ctx.Err() == nil
}, testutil.WaitLong, testutil.IntervalFast)
}

func cmd(t *testing.T, ctx context.Context, args ...string) (*exec.Cmd, string) {
dir := ""
cmd := exec.Command(TestBin, args...)
if len(args) == 0 {
dir = t.TempDir()
//nolint:gosec
cmd = exec.CommandContext(ctx, TestBin, "sh", "-c", fmt.Sprintf("touch %s && sleep 10m", dir))
}
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
t.Cleanup(func() {
// Print output of a command if the test fails.
if t.Failed() {
t.Logf("cmd %q output: %s", cmd.Args, buf.String())
}

if cmd.Process != nil {
_ = cmd.Process.Kill()
}
})
return cmd, dir
}
3 changes: 1 addition & 2 deletions agent/agentexec/cmdtest/main.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package main

import (
"context"
"fmt"
"os"

"github.com/coder/coder/v2/agent/agentexec"
)

func main() {
err := agentexec.CLI(context.Background(), os.Args, os.Environ())
err := agentexec.CLI(os.Args, os.Environ())
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
Expand Down
27 changes: 27 additions & 0 deletions agent/agentexec/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package agentexec_test

import (
"fmt"
"os"
"os/exec"
"testing"
)

const TestBin = "/tmp/agent-test"

func TestMain(m *testing.M) {
buildBinary()

os.Exit(m.Run())
}

func buildBinary() {
out, err := exec.Command("go", "build", "-o", TestBin, "./cmdtest").CombinedOutput()
mustf(err, "build binary: %s", out)
}

func mustf(err error, msg string, args ...any) {
if err != nil {
panic(fmt.Sprintf(msg, args...))
}
}
Loading