Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2152758

Browse files
committed
feat: Add config-ssh and tests for resiliency
1 parent7ed283c commit2152758

File tree

10 files changed

+120
-27
lines changed

10 files changed

+120
-27
lines changed

‎agent/agent_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ func TestAgent(t *testing.T) {
3939
t.Cleanup(func() {
4040
_=conn.Close()
4141
})
42-
client:= agent.Conn{conn}
42+
client:= agent.Conn{
43+
Negotiator:api,
44+
Conn:conn,
45+
}
4346
sshClient,err:=client.SSHClient()
4447
require.NoError(t,err)
4548
session,err:=sshClient.NewSession()
@@ -65,7 +68,10 @@ func TestAgent(t *testing.T) {
6568
t.Cleanup(func() {
6669
_=conn.Close()
6770
})
68-
client:=&agent.Conn{conn}
71+
client:=&agent.Conn{
72+
Negotiator:api,
73+
Conn:conn,
74+
}
6975
sshClient,err:=client.SSHClient()
7076
require.NoError(t,err)
7177
session,err:=sshClient.NewSession()

‎agent/conn.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ import (
88
"golang.org/x/xerrors"
99

1010
"github.com/coder/coder/peer"
11+
"github.com/coder/coder/peerbroker/proto"
1112
)
1213

1314
// Conn wraps a peer connection with helper functions to
1415
// communicate with the agent.
1516
typeConnstruct {
17+
// Negotiator is responsible for exchanging messages.
18+
Negotiator proto.DRPCPeerBrokerClient
19+
1620
*peer.Conn
1721
}
1822

@@ -48,3 +52,8 @@ func (c *Conn) SSHClient() (*ssh.Client, error) {
4852
}
4953
returnssh.NewClient(sshConn,channels,requests),nil
5054
}
55+
56+
func (c*Conn)Close()error {
57+
_=c.Negotiator.DRPCConn().Close()
58+
returnc.Conn.Close()
59+
}

‎cli/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func Root() *cobra.Command {
6464
projects(),
6565
users(),
6666
workspaces(),
67-
workspaceSSH(),
67+
ssh(),
6868
workspaceTunnel(),
6969
)
7070

‎cli/ssh.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
package cli
22

33
import (
4-
"os"
5-
64
"github.com/spf13/cobra"
7-
"golang.org/x/crypto/ssh"
8-
"golang.org/x/term"
5+
gossh"golang.org/x/crypto/ssh"
96
"golang.org/x/xerrors"
107

118
"github.com/coder/coder/coderd/database"
129
"github.com/coder/coder/codersdk"
1310
)
1411

15-
funcworkspaceSSH()*cobra.Command {
12+
funcssh()*cobra.Command {
1613
cmd:=&cobra.Command{
1714
Use:"ssh <workspace> [resource]",
1815
RunE:func(cmd*cobra.Command,args []string)error {
@@ -68,6 +65,7 @@ func workspaceSSH() *cobra.Command {
6865
iferr!=nil {
6966
returnerr
7067
}
68+
deferconn.Close()
7169
sshClient,err:=conn.SSHClient()
7270
iferr!=nil {
7371
returnerr
@@ -77,16 +75,16 @@ func workspaceSSH() *cobra.Command {
7775
iferr!=nil {
7876
returnerr
7977
}
80-
_,_=term.MakeRaw(int(os.Stdin.Fd()))
81-
err=sshSession.RequestPty("xterm-256color",128,128,ssh.TerminalModes{
82-
ssh.OCRNL:1,
78+
79+
err=sshSession.RequestPty("xterm-256color",128,128,gossh.TerminalModes{
80+
gossh.OCRNL:1,
8381
})
8482
iferr!=nil {
8583
returnerr
8684
}
87-
sshSession.Stdin=os.Stdin
88-
sshSession.Stdout=os.Stdout
89-
sshSession.Stderr=os.Stderr
85+
sshSession.Stdin=cmd.InOrStdin()
86+
sshSession.Stdout=cmd.OutOrStdout()
87+
sshSession.Stderr=cmd.OutOrStdout()
9088
err=sshSession.Shell()
9189
iferr!=nil {
9290
returnerr

‎cli/ssh_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package cli_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/google/uuid"
7+
"github.com/stretchr/testify/require"
8+
9+
"cdr.dev/slog"
10+
"cdr.dev/slog/sloggers/slogtest"
11+
"github.com/coder/coder/agent"
12+
"github.com/coder/coder/cli/clitest"
13+
"github.com/coder/coder/coderd/coderdtest"
14+
"github.com/coder/coder/codersdk"
15+
"github.com/coder/coder/peer"
16+
"github.com/coder/coder/provisioner/echo"
17+
"github.com/coder/coder/provisionersdk/proto"
18+
"github.com/coder/coder/pty/ptytest"
19+
)
20+
21+
funcTestSSH(t*testing.T) {
22+
t.Parallel()
23+
t.Run("Echo",func(t*testing.T) {
24+
t.Parallel()
25+
client:=coderdtest.New(t,nil)
26+
user:=coderdtest.CreateFirstUser(t,client)
27+
daemonCloser:=coderdtest.NewProvisionerDaemon(t,client)
28+
agentToken:=uuid.NewString()
29+
version:=coderdtest.CreateProjectVersion(t,client,user.OrganizationID,&echo.Responses{
30+
Parse:echo.ParseComplete,
31+
ProvisionDryRun:echo.ProvisionComplete,
32+
Provision: []*proto.Provision_Response{{
33+
Type:&proto.Provision_Response_Complete{
34+
Complete:&proto.Provision_Complete{
35+
Resources: []*proto.Resource{{
36+
Name:"dev",
37+
Type:"google_compute_instance",
38+
Agent:&proto.Agent{
39+
Id:uuid.NewString(),
40+
Auth:&proto.Agent_Token{
41+
Token:agentToken,
42+
},
43+
},
44+
}},
45+
},
46+
},
47+
}},
48+
})
49+
coderdtest.AwaitProjectVersionJob(t,client,version.ID)
50+
project:=coderdtest.CreateProject(t,client,user.OrganizationID,version.ID)
51+
workspace:=coderdtest.CreateWorkspace(t,client,"",project.ID)
52+
coderdtest.AwaitWorkspaceBuildJob(t,client,workspace.LatestBuild.ID)
53+
daemonCloser.Close()
54+
agentClient:=codersdk.New(client.URL)
55+
agentClient.SessionToken=agentToken
56+
agentCloser:=agent.New(agentClient.ListenWorkspaceAgent,&peer.ConnOptions{
57+
Logger:slogtest.Make(t,nil).Leveled(slog.LevelDebug),
58+
})
59+
deferagentCloser.Close()
60+
coderdtest.AwaitWorkspaceAgents(t,client,workspace.LatestBuild.ID)
61+
62+
cmd,root:=clitest.New(t,"ssh",workspace.Name)
63+
clitest.SetupConfig(t,client,root)
64+
doneChan:=make(chanstruct{})
65+
pty:=ptytest.New(t)
66+
cmd.SetIn(pty.Input())
67+
cmd.SetOut(pty.Output())
68+
gofunc() {
69+
deferclose(doneChan)
70+
err:=cmd.Execute()
71+
require.NoError(t,err)
72+
}()
73+
// Shells on Mac, Windows, and Linux all exit shells with the "exit" command.
74+
pty.WriteLine("exit")
75+
<-doneChan
76+
})
77+
}

‎cli/workspaces.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func workspaces() *cobra.Command {
1818
cmd.AddCommand(workspaceShow())
1919
cmd.AddCommand(workspaceStop())
2020
cmd.AddCommand(workspaceStart())
21-
cmd.AddCommand(workspaceSSH())
21+
cmd.AddCommand(ssh())
2222
cmd.AddCommand(workspaceUpdate())
2323

2424
returncmd

‎coderd/coderdtest/coderdtest.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ func AwaitWorkspaceAgents(t *testing.T, client *codersdk.Client, build uuid.UUID
251251
ifresource.Agent==nil {
252252
continue
253253
}
254+
// fmt.Printf("resources: %+v\n", resource.Agent)
254255
ifresource.Agent.FirstConnectedAt==nil {
255256
returnfalse
256257
}

‎codersdk/workspaceresources.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,9 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, resource uuid.UUID, ice
133133
iferr!=nil {
134134
returnnil,xerrors.Errorf("dial peer: %w",err)
135135
}
136-
gofunc() {
137-
// The stream is kept alive to renegotiate the RTC connection
138-
// if need-be. The calling context can be canceled to end
139-
// the negotiation stream, but not the peer connection.
140-
<-peerConn.Closed()
141-
_=conn.Close(websocket.StatusNormalClosure,"")
142-
}()
143136
return&agent.Conn{
144-
Conn:peerConn,
137+
Negotiator:client,
138+
Conn:peerConn,
145139
},nil
146140
}
147141

‎peerbroker/dial.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package peerbroker
22

33
import (
4+
"context"
5+
"errors"
6+
"io"
47
"reflect"
58

69
"github.com/pion/webrtc/v3"
@@ -54,6 +57,11 @@ func Dial(stream proto.DRPCPeerBroker_NegotiateConnectionClient, iceServers []we
5457
for {
5558
serverToClientMessage,err:=stream.Recv()
5659
iferr!=nil {
60+
// p2p connections should never die if this stream does due
61+
// to proper closure or context cancellation!
62+
iferrors.Is(err,io.EOF)||errors.Is(err,context.Canceled) {
63+
return
64+
}
5765
_=peerConn.CloseWithError(xerrors.Errorf("recv: %w",err))
5866
return
5967
}

‎peerbroker/listen.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,10 @@ func (b *peerBrokerService) NegotiateConnection(stream proto.DRPCPeerBroker_Nego
166166
for {
167167
clientToServerMessage,err:=stream.Recv()
168168
iferr!=nil {
169-
iferrors.Is(err,io.EOF) {
170-
break
169+
// p2p connections should never die if this stream does due
170+
// to proper closure or context cancellation!
171+
iferrors.Is(err,io.EOF)||errors.Is(err,context.Canceled) {
172+
returnnil
171173
}
172174
returnpeerConn.CloseWithError(xerrors.Errorf("recv: %w",err))
173175
}
@@ -186,6 +188,4 @@ func (b *peerBrokerService) NegotiateConnection(stream proto.DRPCPeerBroker_Nego
186188
returnpeerConn.CloseWithError(xerrors.Errorf("unhandled message: %s",reflect.TypeOf(clientToServerMessage).String()))
187189
}
188190
}
189-
190-
returnnil
191191
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp