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" diff --git a/agent/agentssh/agentssh.go b/agent/agentssh/agentssh.go index 19831c0d7caa8..e6fafef4f5155 100644 --- a/agent/agentssh/agentssh.go +++ b/agent/agentssh/agentssh.go @@ -256,6 +256,23 @@ 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 + case strings.HasPrefix(kv, "TERM_PROGRAM=Jetbrains.Fleet"): + magicType = MagicSessionTypeJetBrains + default: + continue + } + } + } + // Always force lowercase checking to be case-insensitive. switch strings.ToLower(magicType) { case strings.ToLower(MagicSessionTypeVSCode): @@ -272,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)) +} diff --git a/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx b/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx index 27b391d717360..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"; @@ -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..f969dfd49c823 --- /dev/null +++ b/site/src/components/Icons/JetBrainsIcon.tsx @@ -0,0 +1,22 @@ +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..8f2d529d4dbec --- /dev/null +++ b/site/static/icon/jetbrains.svg @@ -0,0 +1 @@ + \ No newline at end of file