Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fc471eb

Browse files
authored
fix: handle vscodessh style workspace names in coder ssh (#17154)
Fixes an issue where old ssh configs that use the `owner--workspace--agent` format will fail to properly use the `coder ssh` command since we migrated off the `coder vscodessh` command.
1 parent 743d308 commit fc471eb

File tree

2 files changed

+74
-5
lines changed

2 files changed

+74
-5
lines changed

cli/ssh.go

+30-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"os"
1414
"os/exec"
1515
"path/filepath"
16+
"regexp"
1617
"slices"
1718
"strconv"
1819
"strings"
@@ -57,6 +58,7 @@ var (
5758
autostopNotifyCountdown = []time.Duration{30 * time.Minute}
5859
// gracefulShutdownTimeout is the timeout, per item in the stack of things to close
5960
gracefulShutdownTimeout = 2 * time.Second
61+
workspaceNameRe = regexp.MustCompile(`[/.]+|--`)
6062
)
6163

6264
func (r *RootCmd) ssh() *serpent.Command {
@@ -200,10 +202,9 @@ func (r *RootCmd) ssh() *serpent.Command {
200202
parsedEnv = append(parsedEnv, [2]string{k, v})
201203
}
202204

203-
namedWorkspace := strings.TrimPrefix(inv.Args[0], hostPrefix)
204-
// Support "--" as a delimiter between owner and workspace name
205-
namedWorkspace = strings.Replace(namedWorkspace, "--", "/", 1)
206-
205+
workspaceInput := strings.TrimPrefix(inv.Args[0], hostPrefix)
206+
// convert workspace name format into owner/workspace.agent
207+
namedWorkspace := normalizeWorkspaceInput(workspaceInput)
207208
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, namedWorkspace)
208209
if err != nil {
209210
return err
@@ -1413,3 +1414,28 @@ func collectNetworkStats(ctx context.Context, agentConn *workspacesdk.AgentConn,
14131414
DownloadBytesSec: int64(downloadSecs),
14141415
}, nil
14151416
}
1417+
1418+
// Converts workspace name input to owner/workspace.agent format
1419+
// Possible valid input formats:
1420+
// workspace
1421+
// owner/workspace
1422+
// owner--workspace
1423+
// owner/workspace--agent
1424+
// owner/workspace.agent
1425+
// owner--workspace--agent
1426+
// owner--workspace.agent
1427+
func normalizeWorkspaceInput(input string) string {
1428+
// Split on "/", "--", and "."
1429+
parts := workspaceNameRe.Split(input, -1)
1430+
1431+
switch len(parts) {
1432+
case 1:
1433+
return input // "workspace"
1434+
case 2:
1435+
return fmt.Sprintf("%s/%s", parts[0], parts[1]) // "owner/workspace"
1436+
case 3:
1437+
return fmt.Sprintf("%s/%s.%s", parts[0], parts[1], parts[2]) // "owner/workspace.agent"
1438+
default:
1439+
return input // Fallback
1440+
}
1441+
}

cli/ssh_test.go

+44-1
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,11 @@ func setupWorkspaceForAgent(t *testing.T, mutations ...func([]*proto.Agent) []*p
6363
client, store := coderdtest.NewWithDatabase(t, nil)
6464
client.SetLogger(testutil.Logger(t).Named("client"))
6565
first := coderdtest.CreateFirstUser(t, client)
66-
userClient, user := coderdtest.CreateAnotherUser(t, client, first.OrganizationID)
66+
userClient, user := coderdtest.CreateAnotherUserMutators(t, client, first.OrganizationID, nil, func(r *codersdk.CreateUserRequestWithOrgs) {
67+
r.Username = "myuser"
68+
})
6769
r := dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{
70+
Name: "myworkspace",
6871
OrganizationID: first.OrganizationID,
6972
OwnerID: user.ID,
7073
}).WithAgent(mutations...).Do()
@@ -98,6 +101,46 @@ func TestSSH(t *testing.T) {
98101
pty.WriteLine("exit")
99102
<-cmdDone
100103
})
104+
t.Run("WorkspaceNameInput", func(t *testing.T) {
105+
t.Parallel()
106+
107+
cases := []string{
108+
"myworkspace",
109+
"myuser/myworkspace",
110+
"myuser--myworkspace",
111+
"myuser/myworkspace--dev",
112+
"myuser/myworkspace.dev",
113+
"myuser--myworkspace--dev",
114+
"myuser--myworkspace.dev",
115+
}
116+
117+
for _, tc := range cases {
118+
t.Run(tc, func(t *testing.T) {
119+
t.Parallel()
120+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
121+
defer cancel()
122+
123+
client, workspace, agentToken := setupWorkspaceForAgent(t)
124+
125+
inv, root := clitest.New(t, "ssh", tc)
126+
clitest.SetupConfig(t, client, root)
127+
pty := ptytest.New(t).Attach(inv)
128+
129+
cmdDone := tGo(t, func() {
130+
err := inv.WithContext(ctx).Run()
131+
assert.NoError(t, err)
132+
})
133+
pty.ExpectMatch("Waiting")
134+
135+
_ = agenttest.New(t, client.URL, agentToken)
136+
coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID)
137+
138+
// Shells on Mac, Windows, and Linux all exit shells with the "exit" command.
139+
pty.WriteLine("exit")
140+
<-cmdDone
141+
})
142+
}
143+
})
101144
t.Run("StartStoppedWorkspace", func(t *testing.T) {
102145
t.Parallel()
103146

0 commit comments

Comments
 (0)