mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
fix: track JetBrains connections (#10968)
* feat: implement jetbrains agentssh tracking Based on tcp forwarding instead of ssh connections * Add JetBrains tracking to bottom bar
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package agent_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
@ -152,7 +153,7 @@ func TestAgent_Stats_Magic(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, strings.TrimSpace(string(output)))
|
||||
})
|
||||
t.Run("Tracks", func(t *testing.T) {
|
||||
t.Run("TracksVSCode", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if runtime.GOOS == "window" {
|
||||
t.Skip("Sleeping for infinity doesn't work on Windows")
|
||||
@ -191,6 +192,77 @@ func TestAgent_Stats_Magic(t *testing.T) {
|
||||
err = session.Wait()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("TracksJetBrains", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skip("JetBrains tracking is only supported on Linux")
|
||||
}
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
// JetBrains tracking works by looking at the process name listening on the
|
||||
// forwarded port. If the process's command line includes the magic string
|
||||
// we are looking for, then we assume it is a JetBrains editor. So when we
|
||||
// connect to the port we must ensure the process includes that magic string
|
||||
// to fool the agent into thinking this is JetBrains. To do this we need to
|
||||
// spawn an external process (in this case a simple echo server) so we can
|
||||
// control the process name. The -D here is just to mimic how Java options
|
||||
// are set but is not necessary as the agent looks only for the magic
|
||||
// string itself anywhere in the command.
|
||||
_, b, _, ok := runtime.Caller(0)
|
||||
require.True(t, ok)
|
||||
dir := filepath.Join(filepath.Dir(b), "../scripts/echoserver/main.go")
|
||||
echoServerCmd := exec.Command("go", "run", dir,
|
||||
"-D", agentssh.MagicProcessCmdlineJetBrains)
|
||||
stdout, err := echoServerCmd.StdoutPipe()
|
||||
require.NoError(t, err)
|
||||
err = echoServerCmd.Start()
|
||||
require.NoError(t, err)
|
||||
defer echoServerCmd.Process.Kill()
|
||||
|
||||
// The echo server prints its port as the first line.
|
||||
sc := bufio.NewScanner(stdout)
|
||||
sc.Scan()
|
||||
remotePort := sc.Text()
|
||||
|
||||
//nolint:dogsled
|
||||
conn, _, stats, _, _ := setupAgent(t, agentsdk.Manifest{}, 0)
|
||||
sshClient, err := conn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
tunneledConn, err := sshClient.Dial("tcp", fmt.Sprintf("127.0.0.1:%s", remotePort))
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
// always close on failure of test
|
||||
_ = conn.Close()
|
||||
_ = tunneledConn.Close()
|
||||
})
|
||||
|
||||
var s *agentsdk.Stats
|
||||
require.Eventuallyf(t, func() bool {
|
||||
var ok bool
|
||||
s, ok = <-stats
|
||||
return ok && s.ConnectionCount > 0 &&
|
||||
s.SessionCountJetBrains == 1
|
||||
}, testutil.WaitLong, testutil.IntervalFast,
|
||||
"never saw stats with conn open: %+v", s,
|
||||
)
|
||||
|
||||
// Kill the server and connection after checking for the echo.
|
||||
requireEcho(t, tunneledConn)
|
||||
_ = echoServerCmd.Process.Kill()
|
||||
_ = tunneledConn.Close()
|
||||
|
||||
require.Eventuallyf(t, func() bool {
|
||||
var ok bool
|
||||
s, ok = <-stats
|
||||
return ok && s.ConnectionCount == 0 &&
|
||||
s.SessionCountJetBrains == 0
|
||||
}, testutil.WaitLong, testutil.IntervalFast,
|
||||
"never saw stats after conn closes: %+v", s,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAgent_SessionExec(t *testing.T) {
|
||||
|
Reference in New Issue
Block a user