8000 feat: Add SSH agent forwarding support to coder agent by mafredri · Pull Request #1548 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

feat: Add SSH agent forwarding support to coder agent #1548

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 25, 2022
Prev Previous commit
Next Next commit
fix: Fix test flakes and implement Deans suggestion for helpers
  • Loading branch information
mafredri committed May 24, 2022
commit 5044d11d9b12833fe518ecc5035149aeb0f3310d
41 changes: 25 additions & 16 deletions cli/ssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestSSH(t *testing.T) {
cmd.SetIn(pty.Input())
cmd.SetErr(pty.Output())
cmd.SetOut(pty.Output())
tGo(t, func() {
cmdDone := tGo(t, func() {
err := cmd.Execute()
assert.NoError(t, err)
})
Expand All @@ -90,14 +90,13 @@ func TestSSH(t *testing.T) {

// Shells on Mac, Windows, and Linux all exit shells with the "exit" command.
pty.WriteLine("exit")
// Wait before closing agent to give `coder ssh` time to exit cleanly.
time.Sleep(3 * time.Second)
<-cmdDone
})
t.Run("Stdio", func(t *testing.T) {
t.Parallel()
client, workspace, agentToken := setupWorkspaceForSSH(t)

tGoContext(t, func(ctx context.Context) {
_, _ = tGoContext(t, func(ctx context.Context) {
// Run this async so the SSH command has to wait for
// the build and agent to connect!
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
Expand All @@ -118,7 +117,7 @@ func TestSSH(t *testing.T) {
cmd.SetIn(clientOutput)
cmd.SetOut(serverInput)
cmd.SetErr(io.Discard)
tGo(t, func() {
cmdDone := tGo(t, func() {
err := cmd.Execute()
assert.NoError(t, err)
})
Expand All @@ -143,6 +142,8 @@ func TestSSH(t *testing.T) {
err = sshClient.Close()
require.NoError(t, err)
_ = clientOutput.Close()

<-cmdDone
})
//nolint:paralleltest // Disabled due to use of t.Setenv.
t.Run("ForwardAgent", func(t *testing.T) {
Expand All @@ -152,7 +153,7 @@ func TestSSH(t *testing.T) {

client, workspace, agentToken := setupWorkspaceForSSH(t)

tGoContext(t, func(ctx context.Context) {
_, _ = tGoContext(t, func(ctx context.Context) {
// Run this async so the SSH command has to wait for
// the build and agent to connect!
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
Expand All @@ -179,7 +180,7 @@ func TestSSH(t *testing.T) {
l, err := net.Listen("unix", agentSock)
require.NoError(t, err)
defer l.Close()
tGo(t, func() {
_ = tGo(t, func() {
for {
fd, err := l.Accept()
if err != nil {
Expand All @@ -205,7 +206,7 @@ func TestSSH(t *testing.T) {
cmd.SetIn(pty.Input())
cmd.SetOut(pty.Output())
cmd.SetErr(io.Discard)
tGo(t, func() {
cmdDone := tGo(t, func() {
err := cmd.Execute()
assert.NoError(t, err)
})
Expand All @@ -223,43 +224,51 @@ func TestSSH(t *testing.T) {

// And we're done.
pty.WriteLine("exit")
<-cmdDone
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: This is such a cool test.

}

// tGoContext runs fn in a goroutine passing a context that will be
// canceled on test completion and wait until fn has finished executing.
// Done and cancel are returned for optionally waiting until completion
// or early cancellation.
//
// NOTE(mafredri): This could be moved to a helper library.
func tGoContext(t *testing.T, fn func(context.Context)) {
func tGoContext(t *testing.T, fn func(context.Context)) (done <-chan struct{}, cancel context.CancelFunc) {
t.Helper()

ctx, cancel := context.WithCancel(context.Background())
done := make(chan struct{})
doneC := make(chan struct{})
t.Cleanup(func() {
cancel()
<-done
})
go func() {
fn(ctx)
close(done)
close(doneC)
}()

ret 8361 urn doneC, cancel
}

// tGo runs fn in a goroutine and waits until fn has completed before
// test completion.
// test completion. Done is returned for optionally waiting for fn to
// exit.
//
// NOTE(mafredri): This could be moved to a helper library.
func tGo(t *testing.T, fn func()) {
func tGo(t *testing.T, fn func()) (done <-chan struct{}) {
t.Helper()

done := make(chan struct{})
doneC := make(chan struct{})
t.Cleanup(func() {
<-done
<-doneC
})
go func() {
fn()
close(done)
close(doneC)
}()

return doneC
}

type stdioConn struct {
Expand Down
0