From 398085fd03bf053eaa8bfa780d6b30cd8ad1b4b7 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 2 Nov 2023 11:20:52 +0000 Subject: [PATCH 01/10] fix(agent): track JetBrains Gateway and Fleet --- agent/agent_test.go | 41 +++++++++++++++++++++++++++++++++++++- agent/agentssh/agentssh.go | 20 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/agent/agent_test.go b/agent/agent_test.go index b54f877fcdab9..f22d88dbbace4 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -153,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("VSCode", func(t *testing.T) { t.Parallel() if runtime.GOOS == "window" { t.Skip("Sleeping for infinity doesn't work on Windows") @@ -192,6 +192,45 @@ func TestAgent_Stats_Magic(t *testing.T) { err = session.Wait() require.NoError(t, err) }) + t.Run("JetBrains", func(t *testing.T) { + t.Parallel() + if runtime.GOOS == "window" { + t.Skip("Sleeping for infinity doesn't work on Windows") + } + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + //nolint:dogsled + conn, _, stats, _, _ := setupAgent(t, agentsdk.Manifest{}, 0) + sshClient, err := conn.SSHClient(ctx) + require.NoError(t, err) + defer sshClient.Close() + session, err := sshClient.NewSession() + require.NoError(t, err) + session.Setenv(agentssh.MagicSessionTypeEnvironmentVariable, agentssh.MagicSessionTypeJetBrains) + defer session.Close() + stdin, err := session.StdinPipe() + require.NoError(t, err) + err = session.Shell() + require.NoError(t, err) + var s *agentsdk.Stats + require.Eventuallyf(t, func() bool { + var ok bool + s, ok = <-stats + return ok && s.ConnectionCount > 0 && s.RxBytes > 0 && s.TxBytes > 0 && + // Ensure that the connection didn't count as a "normal" SSH session. + // This was a special one, so it should be labeled specially in the stats! + s.SessionCountVSCode == 1 && + // Ensure that connection latency is being counted! + // If it isn't, it's set to -1. + s.ConnectionMedianLatencyMS >= 0 + }, testutil.WaitLong, testutil.IntervalFast, + "never saw stats: %+v", s, + ) + // The shell will automatically exit if there is no stdin! + _ = stdin.Close() + err = session.Wait() + require.NoError(t, err) + }) } func TestAgent_SessionExec(t *testing.T) { diff --git a/agent/agentssh/agentssh.go b/agent/agentssh/agentssh.go index 19831c0d7caa8..806bc7a1cc76d 100644 --- a/agent/agentssh/agentssh.go +++ b/agent/agentssh/agentssh.go @@ -256,6 +256,26 @@ func (s *Server) sessionStart(session ssh.Session, extraEnv []string) (retErr er env = append(env[:index], env[index+1:]...) } + // Check for JetBrains Gateway and Fleet if MagicType is empty + // JetBrains Geteway does not support SetEnv so this is not being set. See: #9673 + // As a workaround, we can check for the TERMINAL_EMULATOR env variable to identify JetBrains Gateway + // and TERM_PROGRAM for JetBrains Fleet + if magicType == "" { + for _, kv := range env { + switch { + case strings.HasPrefix(kv, "TERMINAL_EMULATOR=JetBrains-JediTerm"): + magicType = MagicSessionTypeJetBrains // Set as JetBrains Gateway type + break + case strings.HasPrefix(kv, "TERM_PROGRAM=Jetbrains.Fleet"): + magicType = MagicSessionTypeJetBrains // Set as Fleet type + break + // leave magicType as empty string if not found + default: + continue + } + } + } + // Always force lowercase checking to be case-insensitive. switch strings.ToLower(magicType) { case strings.ToLower(MagicSessionTypeVSCode): From d9e915c7f6f492d97c5dfbf97f9a832a083e11bf Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 2 Nov 2023 11:41:44 +0000 Subject: [PATCH 02/10] `make lint` --- agent/agentssh/agentssh.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/agent/agentssh/agentssh.go b/agent/agentssh/agentssh.go index 806bc7a1cc76d..daa3b80f46c23 100644 --- a/agent/agentssh/agentssh.go +++ b/agent/agentssh/agentssh.go @@ -265,11 +265,8 @@ func (s *Server) sessionStart(session ssh.Session, extraEnv []string) (retErr er switch { case strings.HasPrefix(kv, "TERMINAL_EMULATOR=JetBrains-JediTerm"): magicType = MagicSessionTypeJetBrains // Set as JetBrains Gateway type - break case strings.HasPrefix(kv, "TERM_PROGRAM=Jetbrains.Fleet"): magicType = MagicSessionTypeJetBrains // Set as Fleet type - break - // leave magicType as empty string if not found default: continue } From 63fb75ee443a5536db4f7c6aa8806cbe41ca3aa2 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 2 Nov 2023 13:04:47 +0000 Subject: [PATCH 03/10] typos --- .github/workflows/typos.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/typos.toml b/.github/workflows/typos.toml index a031f622d54df..0d524c886c1b7 100644 --- a/.github/workflows/typos.toml +++ b/.github/workflows/typos.toml @@ -1,6 +1,5 @@ [default.extend-identifiers] alog = "alog" -Jetbrains = "JetBrains" IST = "IST" MacOS = "macOS" AKS = "AKS" From 6186e07d7b545bea6bac9aa7a5016661f434bd18 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 2 Nov 2023 13:13:01 +0000 Subject: [PATCH 04/10] remove test --- agent/agent_test.go | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/agent/agent_test.go b/agent/agent_test.go index f22d88dbbace4..b54f877fcdab9 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -153,7 +153,7 @@ func TestAgent_Stats_Magic(t *testing.T) { require.NoError(t, err) require.Equal(t, expected, strings.TrimSpace(string(output))) }) - t.Run("VSCode", func(t *testing.T) { + t.Run("Tracks", func(t *testing.T) { t.Parallel() if runtime.GOOS == "window" { t.Skip("Sleeping for infinity doesn't work on Windows") @@ -192,45 +192,6 @@ func TestAgent_Stats_Magic(t *testing.T) { err = session.Wait() require.NoError(t, err) }) - t.Run("JetBrains", func(t *testing.T) { - t.Parallel() - if runtime.GOOS == "window" { - t.Skip("Sleeping for infinity doesn't work on Windows") - } - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - //nolint:dogsled - conn, _, stats, _, _ := setupAgent(t, agentsdk.Manifest{}, 0) - sshClient, err := conn.SSHClient(ctx) - require.NoError(t, err) - defer sshClient.Close() - session, err := sshClient.NewSession() - require.NoError(t, err) - session.Setenv(agentssh.MagicSessionTypeEnvironmentVariable, agentssh.MagicSessionTypeJetBrains) - defer session.Close() - stdin, err := session.StdinPipe() - require.NoError(t, err) - err = session.Shell() - require.NoError(t, err) - var s *agentsdk.Stats - require.Eventuallyf(t, func() bool { - var ok bool - s, ok = <-stats - return ok && s.ConnectionCount > 0 && s.RxBytes > 0 && s.TxBytes > 0 && - // Ensure that the connection didn't count as a "normal" SSH session. - // This was a special one, so it should be labeled specially in the stats! - s.SessionCountVSCode == 1 && - // Ensure that connection latency is being counted! - // If it isn't, it's set to -1. - s.ConnectionMedianLatencyMS >= 0 - }, testutil.WaitLong, testutil.IntervalFast, - "never saw stats: %+v", s, - ) - // The shell will automatically exit if there is no stdin! - _ = stdin.Close() - err = session.Wait() - require.NoError(t, err) - }) } func TestAgent_SessionExec(t *testing.T) { From cd30f28eb682001f54c9904a3b0d00330527ecde Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 2 Nov 2023 13:14:02 +0000 Subject: [PATCH 05/10] correct comments --- agent/agentssh/agentssh.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/agentssh/agentssh.go b/agent/agentssh/agentssh.go index daa3b80f46c23..6a8aa53db142d 100644 --- a/agent/agentssh/agentssh.go +++ b/agent/agentssh/agentssh.go @@ -264,9 +264,9 @@ func (s *Server) sessionStart(session ssh.Session, extraEnv []string) (retErr er for _, kv := range env { switch { case strings.HasPrefix(kv, "TERMINAL_EMULATOR=JetBrains-JediTerm"): - magicType = MagicSessionTypeJetBrains // Set as JetBrains Gateway type + magicType = MagicSessionTypeJetBrains case strings.HasPrefix(kv, "TERM_PROGRAM=Jetbrains.Fleet"): - magicType = MagicSessionTypeJetBrains // Set as Fleet type + magicType = MagicSessionTypeJetBrains default: continue } From 0238044b1e8c7ac21d198c681898507968d90a99 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 2 Nov 2023 13:31:46 +0000 Subject: [PATCH 06/10] add icon and statusbar --- .../DeploymentBanner/DeploymentBannerView.tsx | 16 ++++++++++ site/src/components/Icons/JetBrainsIcon.tsx | 29 +++++++++++++++++++ site/src/theme/icons.json | 1 + site/static/icon/jetbrains.svg | 24 +++++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 site/src/components/Icons/JetBrainsIcon.tsx create mode 100644 site/static/icon/jetbrains.svg diff --git a/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx b/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx index 27b391d717360..aa447b33c5628 100644 --- a/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx +++ b/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx @@ -37,6 +37,7 @@ import { getDisplayWorkspaceStatus } from "utils/workspace"; import { colors } from "theme/colors"; import { HelpTooltipTitle } from "components/HelpTooltip/HelpTooltip"; import { Stack } from "components/Stack/Stack"; +import { JetBrainsIcon } from "components/Icons/JetBrainsIcon"; export const bannerHeight = 36; @@ -298,6 +299,21 @@ export const DeploymentBannerView: FC = (props) => { + +
+ + {typeof stats?.session_count.jetbrains === "undefined" + ? "-" + : stats?.session_count.jetbrains} +
+
+
diff --git a/site/src/components/Icons/JetBrainsIcon.tsx b/site/src/components/Icons/JetBrainsIcon.tsx new file mode 100644 index 0000000000000..e546aea90e814 --- /dev/null +++ b/site/src/components/Icons/JetBrainsIcon.tsx @@ -0,0 +1,29 @@ +import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon"; + +export const JetBrainsIcon = (props: SvgIconProps) => ( + + + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/site/src/theme/icons.json b/site/src/theme/icons.json index fe8011d7e8685..21a13ae46700e 100644 --- a/site/src/theme/icons.json +++ b/site/src/theme/icons.json @@ -36,6 +36,7 @@ "java.svg", "javascript.svg", "jax.svg", + "jetbrains.svg", "jfrog.svg", "jupyter.svg", "k8s.png", diff --git a/site/static/icon/jetbrains.svg b/site/static/icon/jetbrains.svg new file mode 100644 index 0000000000000..3c0905e3b5c04 --- /dev/null +++ b/site/static/icon/jetbrains.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + From 9429ddddd29104bb0cae31bc0304e8bd3bfb4d41 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 2 Nov 2023 13:32:20 +0000 Subject: [PATCH 07/10] fmt --- site/src/components/Icons/JetBrainsIcon.tsx | 74 +++++++++++++++------ 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/site/src/components/Icons/JetBrainsIcon.tsx b/site/src/components/Icons/JetBrainsIcon.tsx index e546aea90e814..404ee53d4ea71 100644 --- a/site/src/components/Icons/JetBrainsIcon.tsx +++ b/site/src/components/Icons/JetBrainsIcon.tsx @@ -3,26 +3,62 @@ import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon"; export const JetBrainsIcon = (props: SvgIconProps) => ( - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - + + + + + From 7c268a3eb953eee014881d5c66c9d73e675fe9a8 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 2 Nov 2023 13:41:27 +0000 Subject: [PATCH 08/10] `fmt`, `gen` and 'lint' --- .../DeploymentBanner/DeploymentBannerView.tsx | 2 +- site/src/components/Icons/JetBrainsIcon.tsx | 69 ++++--------------- site/static/icon/jetbrains.svg | 25 +------ 3 files changed, 15 insertions(+), 81 deletions(-) diff --git a/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx b/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx index aa447b33c5628..1d60d909f5219 100644 --- a/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx +++ b/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx @@ -13,6 +13,7 @@ import Tooltip from "@mui/material/Tooltip"; import { Link as RouterLink } from "react-router-dom"; import Link from "@mui/material/Link"; import { VSCodeIcon } from "components/Icons/VSCodeIcon"; +import { JetBrainsIcon } from "components/Icons/JetBrainsIcon"; import DownloadIcon from "@mui/icons-material/CloudDownload"; import UploadIcon from "@mui/icons-material/CloudUpload"; import LatencyIcon from "@mui/icons-material/SettingsEthernet"; @@ -37,7 +38,6 @@ import { getDisplayWorkspaceStatus } from "utils/workspace"; import { colors } from "theme/colors"; import { HelpTooltipTitle } from "components/HelpTooltip/HelpTooltip"; import { Stack } from "components/Stack/Stack"; -import { JetBrainsIcon } from "components/Icons/JetBrainsIcon"; export const bannerHeight = 36; diff --git a/site/src/components/Icons/JetBrainsIcon.tsx b/site/src/components/Icons/JetBrainsIcon.tsx index 404ee53d4ea71..30fd64498086a 100644 --- a/site/src/components/Icons/JetBrainsIcon.tsx +++ b/site/src/components/Icons/JetBrainsIcon.tsx @@ -3,63 +3,20 @@ import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon"; export const JetBrainsIcon = (props: SvgIconProps) => ( - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + ); diff --git a/site/static/icon/jetbrains.svg b/site/static/icon/jetbrains.svg index 3c0905e3b5c04..8f2d529d4dbec 100644 --- a/site/static/icon/jetbrains.svg +++ b/site/static/icon/jetbrains.svg @@ -1,24 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file From 31d7461fb1c2d1c1094b828074b088242bbf37da Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 2 Nov 2023 14:05:17 +0000 Subject: [PATCH 09/10] fix icon color --- site/src/components/Icons/JetBrainsIcon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/components/Icons/JetBrainsIcon.tsx b/site/src/components/Icons/JetBrainsIcon.tsx index 30fd64498086a..f969dfd49c823 100644 --- a/site/src/components/Icons/JetBrainsIcon.tsx +++ b/site/src/components/Icons/JetBrainsIcon.tsx @@ -2,7 +2,7 @@ import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon"; export const JetBrainsIcon = (props: SvgIconProps) => ( - + From d4a8f2055f189ccb3a3b454e4d1962c2114585a9 Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Sat, 4 Nov 2023 10:38:27 +0300 Subject: [PATCH 10/10] wip --- agent/agentssh/agentssh.go | 1 + agent/agentssh/agentssh_internal_test.go | 26 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/agent/agentssh/agentssh.go b/agent/agentssh/agentssh.go index 6a8aa53db142d..e6fafef4f5155 100644 --- a/agent/agentssh/agentssh.go +++ b/agent/agentssh/agentssh.go @@ -289,6 +289,7 @@ func (s *Server) sessionStart(session ssh.Session, extraEnv []string) (retErr er } magicTypeLabel := magicTypeMetricLabel(magicType) + fmt.Println("New connection, magic type =", magicType) sshPty, windowSize, isPty := session.Pty() cmd, err := s.CreateCommand(ctx, session.RawCommand(), env) diff --git a/agent/agentssh/agentssh_internal_test.go b/agent/agentssh/agentssh_internal_test.go index aa4cfe0236261..d59a10ea7979c 100644 --- a/agent/agentssh/agentssh_internal_test.go +++ b/agent/agentssh/agentssh_internal_test.go @@ -8,6 +8,7 @@ import ( "io" "net" "testing" + "fmt" gliderssh "github.com/gliderlabs/ssh" "github.com/prometheus/client_golang/prometheus" @@ -19,6 +20,10 @@ import ( "github.com/coder/coder/v2/testutil" "cdr.dev/slog/sloggers/slogtest" + "go.uber.org/atomic" + "github.com/coder/coder/v2/codersdk/agentsdk" + "github.com/coder/coder/v2/codersdk" + ) const longScript = ` @@ -195,3 +200,24 @@ func (testSSHContext) SetValue(_, _ interface{}) { func (testSSHContext) KeepAlive() *gliderssh.SessionKeepAlive { panic("not implemented") } + +func TestSomeSSHServer(t *testing.T) { + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) + defer cancel() + logger := slogtest.Make(t, nil) + + s, err := NewServer(ctx, logger, prometheus.NewRegistry(), afero.NewMemMapFs(), 0, "") + require.NoError(t, err) + defer s.Close() + // Required to not panic on new connection + s.Manifest = atomic.NewPointer(&agentsdk.Manifest{}) + s.ServiceBanner = atomic.NewPointer(&codersdk.ServiceBannerConfig{}) + s.AgentToken = func() string { + return "something" + } + + l, err := net.Listen("tcp", "localhost:1828") + require.NoError(t, err) + fmt.Println(s.Serve(l)) +}