From 8a7b5dcd519b444ec7af884bd562faf8a07f44d1 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Thu, 9 Jan 2025 10:54:54 -0800 Subject: [PATCH] feat: parse structured hostname arguments to "coder ssh" If the argument to `coder ssh` fits the pattern `------`, then parse this instead of treating the argument as a literal workspace name or `/`. This will enable the use of `coder ssh` for the VS Code and JetBrains plugins in combination with wildcard `Host` entries in the SSH config. Part of #14986. --- cli/ssh.go | 12 +++++++++- cli/ssh_test.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/cli/ssh.go b/cli/ssh.go index 7a1d5940bfd01..01265780b1e10 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -180,7 +180,17 @@ func (r *RootCmd) ssh() *serpent.Command { parsedEnv = append(parsedEnv, [2]string{k, v}) } - workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, inv.Args[0]) + namedWorkspace := inv.Args[0] + if parts := strings.Split(namedWorkspace, "--"); len(parts) >= 3 { + owner := parts[1] + name := parts[2] + namedWorkspace = owner + "/" + name + if len(parts) > 3 { + namedWorkspace += "." + parts[3] + } + } + + workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, namedWorkspace) if err != nil { return err } diff --git a/cli/ssh_test.go b/cli/ssh_test.go index bd107852251f7..ce4173e90fc35 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -1482,6 +1482,69 @@ func TestSSH(t *testing.T) { }) } }) + + t.Run("ParseHostname", func(t *testing.T) { + t.Parallel() + client, workspace, agentToken := setupWorkspaceForAgent(t) + _, _ = tGoContext(t, func(ctx context.Context) { + // Run this async so the SSH command has to wait for + // the build and agent to connect! + _ = agenttest.New(t, client.URL, agentToken) + <-ctx.Done() + }) + + clientOutput, clientInput := io.Pipe() + serverOutput, serverInput := io.Pipe() + defer func() { + for _, c := range []io.Closer{clientOutput, clientInput, serverOutput, serverInput} { + _ = c.Close() + } + }() + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + user, err := client.User(ctx, codersdk.Me) + require.NoError(t, err) + + inv, root := clitest.New(t, "ssh", "--stdio", fmt.Sprintf("coder-vscode.coder.prod.netflix.net--%s--%s", user.Username, workspace.Name)) + clitest.SetupConfig(t, client, root) + inv.Stdin = clientOutput + inv.Stdout = serverInput + inv.Stderr = io.Discard + + cmdDone := tGo(t, func() { + err := inv.WithContext(ctx).Run() + assert.NoError(t, err) + }) + + conn, channels, requests, err := ssh.NewClientConn(&stdioConn{ + Reader: serverOutput, + Writer: clientInput, + }, "", &ssh.ClientConfig{ + // #nosec + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + }) + require.NoError(t, err) + defer conn.Close() + + sshClient := ssh.NewClient(conn, channels, requests) + session, err := sshClient.NewSession() + require.NoError(t, err) + defer session.Close() + + command := "sh -c exit" + if runtime.GOOS == "windows" { + command = "cmd.exe /c exit" + } + err = session.Run(command) + require.NoError(t, err) + err = sshClient.Close() + require.NoError(t, err) + _ = clientOutput.Close() + + <-cmdDone + }) } //nolint:paralleltest // This test uses t.Setenv, parent test MUST NOT be parallel. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy