Skip to content

Commit

Permalink
feat: Add support for executing processes with Windows ConPty (#311)
Browse files Browse the repository at this point in the history
* Initial agent

* fix: Use buffered reader in peer to fix ShortBuffer

This prevents a io.ErrShortBuffer from occurring when the byte
slice being read is smaller than the chunks sent from the opposite
pipe.

This makes sense for unordered connections, where transmission is
not guarunteed, but does not make sense for TCP-like connections.

We use a bufio.Reader when ordered to ensure data isn't lost.

* SSH server works!

* Start Windows support

* Something works

* Refactor pty package to support Windows spawn

* SSH server now works on Windows

* Fix non-Windows

* Fix Linux PTY render

* FIx linux build tests

* Remove agent and wintest

* Add test for Windows resize

* Fix linting errors

* Add Windows environment variables

* Add strings import

* Add comment for attrs

* Add goleak

* Add require import
  • Loading branch information
kylecarbs committed Feb 17, 2022
1 parent c2ad91b commit 503d09c
Show file tree
Hide file tree
Showing 32 changed files with 582 additions and 1,140 deletions.
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"drpcserver",
"fatih",
"goleak",
"gossh",
"hashicorp",
"httpmw",
"isatty",
Expand All @@ -51,9 +52,12 @@
"protobuf",
"provisionerd",
"provisionersdk",
"ptty",
"ptytest",
"retrier",
"sdkproto",
"stretchr",
"tcpip",
"tfexec",
"tfstate",
"unconvert",
Expand Down
9 changes: 5 additions & 4 deletions cli/clitest/clitest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/coder/coder/cli/clitest"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/console"
"github.com/coder/coder/pty/ptytest"
)

func TestMain(m *testing.M) {
Expand All @@ -21,11 +21,12 @@ func TestCli(t *testing.T) {
client := coderdtest.New(t)
cmd, config := clitest.New(t)
clitest.SetupConfig(t, client, config)
cons := console.New(t, cmd)
pty := ptytest.New(t)
cmd.SetIn(pty.Input())
cmd.SetOut(pty.Output())
go func() {
err := cmd.Execute()
require.NoError(t, err)
}()
_, err := cons.ExpectString("coder")
require.NoError(t, err)
pty.ExpectMatch("coder")
}
18 changes: 9 additions & 9 deletions cli/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package cli_test
import (
"testing"

"github.com/stretchr/testify/require"

"github.com/coder/coder/cli/clitest"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/console"
"github.com/stretchr/testify/require"
"github.com/coder/coder/pty/ptytest"
)

func TestLogin(t *testing.T) {
Expand All @@ -26,7 +27,9 @@ func TestLogin(t *testing.T) {
// accurately detect Windows ptys when they are not attached to a process:
// https://github.com/mattn/go-isatty/issues/59
root, _ := clitest.New(t, "login", client.URL.String(), "--force-tty")
cons := console.New(t, root)
pty := ptytest.New(t)
root.SetIn(pty.Input())
root.SetOut(pty.Output())
go func() {
err := root.Execute()
require.NoError(t, err)
Expand All @@ -42,12 +45,9 @@ func TestLogin(t *testing.T) {
for i := 0; i < len(matches); i += 2 {
match := matches[i]
value := matches[i+1]
_, err := cons.ExpectString(match)
require.NoError(t, err)
_, err = cons.SendLine(value)
require.NoError(t, err)
pty.ExpectMatch(match)
pty.WriteLine(value)
}
_, err := cons.ExpectString("Welcome to Coder")
require.NoError(t, err)
pty.ExpectMatch("Welcome to Coder")
})
}
22 changes: 11 additions & 11 deletions cli/projectcreate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (

"github.com/coder/coder/cli/clitest"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/console"
"github.com/coder/coder/database"
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
"github.com/coder/coder/pty/ptytest"
)

func TestProjectCreate(t *testing.T) {
Expand All @@ -26,7 +26,9 @@ func TestProjectCreate(t *testing.T) {
cmd, root := clitest.New(t, "projects", "create", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho))
clitest.SetupConfig(t, client, root)
_ = coderdtest.NewProvisionerDaemon(t, client)
console := console.New(t, cmd)
pty := ptytest.New(t)
cmd.SetIn(pty.Input())
cmd.SetOut(pty.Output())
closeChan := make(chan struct{})
go func() {
err := cmd.Execute()
Expand All @@ -43,10 +45,8 @@ func TestProjectCreate(t *testing.T) {
for i := 0; i < len(matches); i += 2 {
match := matches[i]
value := matches[i+1]
_, err := console.ExpectString(match)
require.NoError(t, err)
_, err = console.SendLine(value)
require.NoError(t, err)
pty.ExpectMatch(match)
pty.WriteLine(value)
}
<-closeChan
})
Expand All @@ -73,7 +73,9 @@ func TestProjectCreate(t *testing.T) {
cmd, root := clitest.New(t, "projects", "create", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho))
clitest.SetupConfig(t, client, root)
coderdtest.NewProvisionerDaemon(t, client)
cons := console.New(t, cmd)
pty := ptytest.New(t)
cmd.SetIn(pty.Input())
cmd.SetOut(pty.Output())
closeChan := make(chan struct{})
go func() {
err := cmd.Execute()
Expand All @@ -91,10 +93,8 @@ func TestProjectCreate(t *testing.T) {
for i := 0; i < len(matches); i += 2 {
match := matches[i]
value := matches[i+1]
_, err := cons.ExpectString(match)
require.NoError(t, err)
_, err = cons.SendLine(value)
require.NoError(t, err)
pty.ExpectMatch(match)
pty.WriteLine(value)
}
<-closeChan
})
Expand Down
19 changes: 10 additions & 9 deletions cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/manifoldco/promptui"
"github.com/mattn/go-isatty"
"github.com/spf13/cobra"
"golang.org/x/xerrors"

"github.com/coder/coder/cli/config"
"github.com/coder/coder/coderd"
Expand Down Expand Up @@ -138,14 +137,9 @@ func isTTY(cmd *cobra.Command) bool {
}

func prompt(cmd *cobra.Command, prompt *promptui.Prompt) (string, error) {
var ok bool
prompt.Stdin, ok = cmd.InOrStdin().(io.ReadCloser)
if !ok {
return "", xerrors.New("stdin must be a readcloser")
}
prompt.Stdout, ok = cmd.OutOrStdout().(io.WriteCloser)
if !ok {
return "", xerrors.New("stdout must be a readcloser")
prompt.Stdin = io.NopCloser(cmd.InOrStdin())
prompt.Stdout = readWriteCloser{
Writer: cmd.OutOrStdout(),
}

// The prompt library displays defaults in a jarring way for the user
Expand Down Expand Up @@ -199,3 +193,10 @@ func prompt(cmd *cobra.Command, prompt *promptui.Prompt) (string, error) {

return value, err
}

// readWriteCloser fakes reads, writes, and closing!
type readWriteCloser struct {
io.Reader
io.Writer
io.Closer
}
18 changes: 9 additions & 9 deletions cli/workspacecreate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package cli_test
import (
"testing"

"github.com/stretchr/testify/require"

"github.com/coder/coder/cli/clitest"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/console"
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
"github.com/stretchr/testify/require"
"github.com/coder/coder/pty/ptytest"
)

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

cons := console.New(t, cmd)
pty := ptytest.New(t)
cmd.SetIn(pty.Input())
cmd.SetOut(pty.Output())
closeChan := make(chan struct{})
go func() {
err := cmd.Execute()
Expand All @@ -51,13 +54,10 @@ func TestWorkspaceCreate(t *testing.T) {
for i := 0; i < len(matches); i += 2 {
match := matches[i]
value := matches[i+1]
_, err := cons.ExpectString(match)
require.NoError(t, err)
_, err = cons.SendLine(value)
require.NoError(t, err)
pty.ExpectMatch(match)
pty.WriteLine(value)
}
_, err := cons.ExpectString("Create")
require.NoError(t, err)
pty.ExpectMatch("Create")
<-closeChan
})
}
3 changes: 2 additions & 1 deletion coderd/projectimport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import (
"net/http"
"testing"

"github.com/stretchr/testify/require"

"github.com/coder/coder/coderd"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/database"
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
"github.com/stretchr/testify/require"
)

func TestPostProjectImportByOrganization(t *testing.T) {
Expand Down
5 changes: 3 additions & 2 deletions codersdk/projectimport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import (
"testing"
"time"

"github.com/google/uuid"
"github.com/stretchr/testify/require"

"github.com/coder/coder/coderd"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)

func TestCreateProjectImportJob(t *testing.T) {
Expand Down
107 changes: 0 additions & 107 deletions console/conpty/conpty.go

This file was deleted.

0 comments on commit 503d09c

Please sign in to comment.