Skip to content

Commit 722be6c

Browse files
committed
Refactor pty package to support Windows spawn
1 parent 72b6b09 commit 722be6c

28 files changed

+289
-1525
lines changed

agent/server.go

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ import (
77
"errors"
88
"io"
99
"net"
10-
"os"
10+
"os/exec"
1111
"sync"
12-
"syscall"
1312
"time"
1413

1514
"cdr.dev/slog"
16-
"github.com/ActiveState/termtest/conpty"
15+
"github.com/coder/coder/console/pty"
1716
"github.com/coder/coder/peer"
1817
"github.com/coder/coder/peerbroker"
1918
"github.com/coder/retry"
@@ -71,31 +70,24 @@ func (s *server) init(ctx context.Context) {
7170
sshLogger.Info(ctx, "ssh connection ended", slog.Error(err))
7271
},
7372
Handler: func(session ssh.Session) {
74-
sshPty, windowSize, isPty := session.Pty()
73+
_, windowSize, isPty := session.Pty()
7574
if isPty {
76-
cpty, err := conpty.New(int16(sshPty.Window.Width), int16(sshPty.Window.Height))
77-
if err != nil {
78-
panic(err)
79-
}
80-
_, _, err = cpty.Spawn("C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", []string{}, &syscall.ProcAttr{
81-
Env: os.Environ(),
82-
})
75+
pty, err := pty.Start(exec.Command("powershell.exe"))
8376
if err != nil {
8477
panic(err)
8578
}
8679
go func() {
8780
for win := range windowSize {
88-
err := cpty.Resize(uint16(win.Width), uint16(win.Height))
81+
err := pty.Resize(uint16(win.Width), uint16(win.Height))
8982
if err != nil {
9083
panic(err)
9184
}
9285
}
9386
}()
94-
9587
go func() {
96-
io.Copy(session, cpty)
88+
io.Copy(session, pty.Output())
9789
}()
98-
io.Copy(cpty, session)
90+
io.Copy(pty.Input(), session)
9991
}
10092
},
10193
HostSigners: []ssh.Signer{randomSigner},

cli/login_test.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55

66
"github.com/coder/coder/cli/clitest"
77
"github.com/coder/coder/coderd/coderdtest"
8-
"github.com/coder/coder/console"
8+
"github.com/coder/coder/pty/ptytest"
99
"github.com/stretchr/testify/require"
1010
)
1111

@@ -26,7 +26,9 @@ func TestLogin(t *testing.T) {
2626
// accurately detect Windows ptys when they are not attached to a process:
2727
// https://github.com/mattn/go-isatty/issues/59
2828
root, _ := clitest.New(t, "login", client.URL.String(), "--force-tty")
29-
cons := console.New(t, root)
29+
pty := ptytest.New(t)
30+
root.SetIn(pty.Input())
31+
root.SetOut(pty.Output())
3032
go func() {
3133
err := root.Execute()
3234
require.NoError(t, err)
@@ -42,12 +44,9 @@ func TestLogin(t *testing.T) {
4244
for i := 0; i < len(matches); i += 2 {
4345
match := matches[i]
4446
value := matches[i+1]
45-
_, err := cons.ExpectString(match)
46-
require.NoError(t, err)
47-
_, err = cons.SendLine(value)
48-
require.NoError(t, err)
47+
pty.ExpectMatch(match)
48+
pty.WriteLine(value)
4949
}
50-
_, err := cons.ExpectString("Welcome to Coder")
51-
require.NoError(t, err)
50+
pty.ExpectMatch("Welcome to Coder")
5251
})
5352
}

cli/projectcreate_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import (
77

88
"github.com/coder/coder/cli/clitest"
99
"github.com/coder/coder/coderd/coderdtest"
10-
"github.com/coder/coder/console"
1110
"github.com/coder/coder/database"
1211
"github.com/coder/coder/provisioner/echo"
1312
"github.com/coder/coder/provisionersdk/proto"
13+
"github.com/coder/coder/pty/ptytest"
1414
)
1515

1616
func TestProjectCreate(t *testing.T) {
@@ -26,7 +26,9 @@ func TestProjectCreate(t *testing.T) {
2626
cmd, root := clitest.New(t, "projects", "create", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho))
2727
clitest.SetupConfig(t, client, root)
2828
_ = coderdtest.NewProvisionerDaemon(t, client)
29-
console := console.New(t, cmd)
29+
pty := ptytest.New(t)
30+
cmd.SetIn(pty.Input())
31+
cmd.SetOut(pty.Output())
3032
closeChan := make(chan struct{})
3133
go func() {
3234
err := cmd.Execute()
@@ -43,10 +45,8 @@ func TestProjectCreate(t *testing.T) {
4345
for i := 0; i < len(matches); i += 2 {
4446
match := matches[i]
4547
value := matches[i+1]
46-
_, err := console.ExpectString(match)
47-
require.NoError(t, err)
48-
_, err = console.SendLine(value)
49-
require.NoError(t, err)
48+
pty.ExpectMatch(match)
49+
pty.WriteLine(value)
5050
}
5151
<-closeChan
5252
})
@@ -73,7 +73,9 @@ func TestProjectCreate(t *testing.T) {
7373
cmd, root := clitest.New(t, "projects", "create", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho))
7474
clitest.SetupConfig(t, client, root)
7575
coderdtest.NewProvisionerDaemon(t, client)
76-
cons := console.New(t, cmd)
76+
pty := ptytest.New(t)
77+
cmd.SetIn(pty.Input())
78+
cmd.SetOut(pty.Output())
7779
closeChan := make(chan struct{})
7880
go func() {
7981
err := cmd.Execute()
@@ -91,10 +93,8 @@ func TestProjectCreate(t *testing.T) {
9193
for i := 0; i < len(matches); i += 2 {
9294
match := matches[i]
9395
value := matches[i+1]
94-
_, err := cons.ExpectString(match)
95-
require.NoError(t, err)
96-
_, err = cons.SendLine(value)
97-
require.NoError(t, err)
96+
pty.ExpectMatch(match)
97+
pty.WriteLine(value)
9898
}
9999
<-closeChan
100100
})

cli/root.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,21 @@ func isTTY(cmd *cobra.Command) bool {
139139

140140
func prompt(cmd *cobra.Command, prompt *promptui.Prompt) (string, error) {
141141
var ok bool
142-
prompt.Stdin, ok = cmd.InOrStdin().(io.ReadCloser)
142+
reader, ok := cmd.InOrStdin().(io.Reader)
143143
if !ok {
144144
return "", xerrors.New("stdin must be a readcloser")
145145
}
146-
prompt.Stdout, ok = cmd.OutOrStdout().(io.WriteCloser)
146+
prompt.Stdin = readWriteCloser{
147+
Reader: reader,
148+
}
149+
150+
writer, ok := cmd.OutOrStdout().(io.Writer)
147151
if !ok {
148152
return "", xerrors.New("stdout must be a readcloser")
149153
}
154+
prompt.Stdout = readWriteCloser{
155+
Writer: writer,
156+
}
150157

151158
// The prompt library displays defaults in a jarring way for the user
152159
// by attempting to autocomplete it. This sets no default enabling us
@@ -199,3 +206,10 @@ func prompt(cmd *cobra.Command, prompt *promptui.Prompt) (string, error) {
199206

200207
return value, err
201208
}
209+
210+
// readWriteCloser fakes reads, writes, and closing!
211+
type readWriteCloser struct {
212+
io.Reader
213+
io.Writer
214+
io.Closer
215+
}

cli/workspacecreate_test.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import (
55

66
"github.com/coder/coder/cli/clitest"
77
"github.com/coder/coder/coderd/coderdtest"
8-
"github.com/coder/coder/console"
98
"github.com/coder/coder/provisioner/echo"
109
"github.com/coder/coder/provisionersdk/proto"
10+
"github.com/coder/coder/pty/ptytest"
1111
"github.com/stretchr/testify/require"
1212
)
1313

@@ -36,7 +36,9 @@ func TestWorkspaceCreate(t *testing.T) {
3636
cmd, root := clitest.New(t, "workspaces", "create", project.Name)
3737
clitest.SetupConfig(t, client, root)
3838

39-
cons := console.New(t, cmd)
39+
pty := ptytest.New(t)
40+
cmd.SetIn(pty.Input())
41+
cmd.SetOut(pty.Output())
4042
closeChan := make(chan struct{})
4143
go func() {
4244
err := cmd.Execute()
@@ -51,13 +53,10 @@ func TestWorkspaceCreate(t *testing.T) {
5153
for i := 0; i < len(matches); i += 2 {
5254
match := matches[i]
5355
value := matches[i+1]
54-
_, err := cons.ExpectString(match)
55-
require.NoError(t, err)
56-
_, err = cons.SendLine(value)
57-
require.NoError(t, err)
56+
pty.ExpectMatch(match)
57+
pty.WriteLine(value)
5858
}
59-
_, err := cons.ExpectString("Create")
60-
require.NoError(t, err)
59+
pty.ExpectMatch("Create")
6160
<-closeChan
6261
})
6362
}

console/conpty/conpty.go

Lines changed: 0 additions & 111 deletions
This file was deleted.

0 commit comments

Comments
 (0)