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

Commit71e4544

Browse files
committed
Improve testing of config-ssh
1 parent008ba69 commit71e4544

File tree

5 files changed

+202
-94
lines changed

5 files changed

+202
-94
lines changed

‎agent/agent_test.go

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@ package agent_test
22

33
import (
44
"context"
5+
"fmt"
6+
"io"
7+
"net"
8+
"os/exec"
9+
"path/filepath"
510
"runtime"
11+
"strconv"
612
"strings"
713
"testing"
814

@@ -29,7 +35,8 @@ func TestAgent(t *testing.T) {
2935
t.Parallel()
3036
t.Run("SessionExec",func(t*testing.T) {
3137
t.Parallel()
32-
session:=setupSSH(t)
38+
session:=setupSSHSession(t)
39+
3340
command:="echo test"
3441
ifruntime.GOOS=="windows" {
3542
command="cmd.exe /c echo test"
@@ -41,7 +48,7 @@ func TestAgent(t *testing.T) {
4148

4249
t.Run("GitSSH",func(t*testing.T) {
4350
t.Parallel()
44-
session:=setupSSH(t)
51+
session:=setupSSHSession(t)
4552
command:="sh -c 'echo $GIT_SSH_COMMAND'"
4653
ifruntime.GOOS=="windows" {
4754
command="cmd.exe /c echo %GIT_SSH_COMMAND%"
@@ -53,7 +60,7 @@ func TestAgent(t *testing.T) {
5360

5461
t.Run("SessionTTY",func(t*testing.T) {
5562
t.Parallel()
56-
session:=setupSSH(t)
63+
session:=setupSSHSession(t)
5764
prompt:="$"
5865
command:="bash"
5966
ifruntime.GOOS=="windows" {
@@ -76,9 +83,73 @@ func TestAgent(t *testing.T) {
7683
err=session.Wait()
7784
require.NoError(t,err)
7885
})
86+
87+
t.Run("LocalForwarding",func(t*testing.T) {
88+
t.Parallel()
89+
random,err:=net.Listen("tcp","127.0.0.1:0")
90+
require.NoError(t,err)
91+
_=random.Close()
92+
tcpAddr,valid:=random.Addr().(*net.TCPAddr)
93+
require.True(t,valid)
94+
randomPort:=tcpAddr.Port
95+
96+
local,err:=net.Listen("tcp","127.0.0.1:0")
97+
require.NoError(t,err)
98+
tcpAddr,valid=local.Addr().(*net.TCPAddr)
99+
require.True(t,valid)
100+
localPort:=tcpAddr.Port
101+
done:=make(chanstruct{})
102+
gofunc() {
103+
conn,err:=local.Accept()
104+
require.NoError(t,err)
105+
_=conn.Close()
106+
close(done)
107+
}()
108+
109+
err=setupSSHCommand(t, []string{"-L",fmt.Sprintf("%d:127.0.0.1:%d",randomPort,localPort)}, []string{"echo","test"}).Start()
110+
require.NoError(t,err)
111+
112+
conn,err:=net.Dial("tcp","127.0.0.1:"+strconv.Itoa(localPort))
113+
require.NoError(t,err)
114+
conn.Close()
115+
<-done
116+
})
79117
}
80118

81-
funcsetupSSH(t*testing.T)*ssh.Session {
119+
funcsetupSSHCommand(t*testing.T,beforeArgs []string,afterArgs []string)*exec.Cmd {
120+
agentConn:=setupAgent(t)
121+
socket:=filepath.Join(t.TempDir(),"ssh")
122+
listener,err:=net.Listen("unix",socket)
123+
require.NoError(t,err)
124+
gofunc() {
125+
for {
126+
conn,err:=listener.Accept()
127+
iferr!=nil {
128+
return
129+
}
130+
ssh,err:=agentConn.SSH()
131+
require.NoError(t,err)
132+
goio.Copy(conn,ssh)
133+
goio.Copy(ssh,conn)
134+
}
135+
}()
136+
t.Cleanup(func() {
137+
_=listener.Close()
138+
})
139+
args:=append(beforeArgs,"-o","ProxyCommand socat - UNIX-CLIENT:"+socket,"-o","StrictHostKeyChecking=no","host")
140+
args=append(args,afterArgs...)
141+
returnexec.Command("ssh",args...)
142+
}
143+
144+
funcsetupSSHSession(t*testing.T)*ssh.Session {
145+
sshClient,err:=setupAgent(t).SSHClient()
146+
require.NoError(t,err)
147+
session,err:=sshClient.NewSession()
148+
require.NoError(t,err)
149+
returnsession
150+
}
151+
152+
funcsetupAgent(t*testing.T)*agent.Conn {
82153
client,server:=provisionersdk.TransportPipe()
83154
closer:=agent.New(func(ctx context.Context,opts*peer.ConnOptions) (*peerbroker.Listener,error) {
84155
returnpeerbroker.Listen(server,nil,opts)
@@ -100,14 +171,9 @@ func setupSSH(t *testing.T) *ssh.Session {
100171
t.Cleanup(func() {
101172
_=conn.Close()
102173
})
103-
agentClient:=&agent.Conn{
174+
175+
return&agent.Conn{
104176
Negotiator:api,
105177
Conn:conn,
106178
}
107-
sshClient,err:=agentClient.SSHClient()
108-
require.NoError(t,err)
109-
session,err:=sshClient.NewSession()
110-
require.NoError(t,err)
111-
112-
returnsession
113179
}

‎cli/configssh.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ const sshEndToken = "# ------------END-CODER------------"
3131

3232
funcconfigSSH()*cobra.Command {
3333
var (
34-
binaryFilestring
3534
sshConfigFilestring
35+
sshOptions []string
3636
)
3737
cmd:=&cobra.Command{
3838
Use:"config-ssh",
@@ -62,11 +62,9 @@ func configSSH() *cobra.Command {
6262
returnxerrors.New("You don't have any workspaces!")
6363
}
6464

65-
ifbinaryFile=="" {
66-
binaryFile,err=currentBinPath(cmd)
67-
iferr!=nil {
68-
returnerr
69-
}
65+
binaryFile,err:=currentBinPath(cmd)
66+
iferr!=nil {
67+
returnerr
7068
}
7169

7270
root:=createConfig(cmd)
@@ -90,13 +88,19 @@ func configSSH() *cobra.Command {
9088
iflen(resource.Agents)>1 {
9189
hostname+="."+agent.Name
9290
}
93-
sshConfigContent+=strings.Join([]string{
91+
configOptions:=[]string{
9492
"Host coder."+hostname,
9593
"\tHostName coder."+hostname,
94+
}
95+
for_,option:=rangesshOptions {
96+
configOptions=append(configOptions,"\t"+option)
97+
}
98+
configOptions=append(configOptions,
9699
fmt.Sprintf("\tProxyCommand %q --global-config %q ssh --stdio %s",binaryFile,root,hostname),
97100
"\tConnectTimeout=0",
98101
"\tStrictHostKeyChecking=no",
99-
},"\n")+"\n"
102+
)
103+
sshConfigContent+=strings.Join(configOptions,"\n")+"\n"
100104
sshConfigContentMutex.Unlock()
101105
}
102106
}
@@ -122,9 +126,8 @@ func configSSH() *cobra.Command {
122126
returnnil
123127
},
124128
}
125-
cliflag.StringVarP(cmd.Flags(),&binaryFile,"binary-file","","CODER_BINARY_PATH","","Specifies the path to a Coder binary.")
126-
_=cmd.Flags().MarkHidden("binary-file")
127129
cliflag.StringVarP(cmd.Flags(),&sshConfigFile,"ssh-config-file","","CODER_SSH_CONFIG_FILE","~/.ssh/config","Specifies the path to an SSH config.")
130+
cmd.Flags().StringArrayVarP(&sshOptions,"ssh-option","o", []string{},"Specifies additional options embedded in each host.")
128131

129132
returncmd
130133
}

‎cli/configssh_test.go

Lines changed: 96 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
package cli_test
22

33
import (
4+
"context"
5+
"io"
6+
"net"
47
"os"
58
"os/exec"
69
"path/filepath"
710
"strings"
811
"testing"
9-
"time"
1012

11-
"cdr.dev/slog/sloggers/slogtest"
1213
"github.com/google/uuid"
1314
"github.com/stretchr/testify/require"
1415

16+
"cdr.dev/slog/sloggers/slogtest"
17+
1518
"github.com/coder/coder/agent"
1619
"github.com/coder/coder/cli/clitest"
1720
"github.com/coder/coder/coderd/coderdtest"
@@ -24,82 +27,103 @@ import (
2427

2528
funcTestConfigSSH(t*testing.T) {
2629
t.Parallel()
27-
binPath:=filepath.Join(t.TempDir(),"coder")
28-
_,err:=exec.Command("go","build","-o",binPath,"github.com/coder/coder/cmd/coder").CombinedOutput()
29-
require.NoError(t,err)
30-
31-
t.Run("Dial",func(t*testing.T) {
32-
t.Parallel()
33-
client:=coderdtest.New(t,nil)
34-
user:=coderdtest.CreateFirstUser(t,client)
35-
coderdtest.NewProvisionerDaemon(t,client)
36-
authToken:=uuid.NewString()
37-
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,&echo.Responses{
38-
Parse:echo.ParseComplete,
39-
ProvisionDryRun: []*proto.Provision_Response{{
40-
Type:&proto.Provision_Response_Complete{
41-
Complete:&proto.Provision_Complete{
42-
Resources: []*proto.Resource{{
30+
client:=coderdtest.New(t,nil)
31+
user:=coderdtest.CreateFirstUser(t,client)
32+
coderdtest.NewProvisionerDaemon(t,client)
33+
authToken:=uuid.NewString()
34+
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,&echo.Responses{
35+
Parse:echo.ParseComplete,
36+
ProvisionDryRun: []*proto.Provision_Response{{
37+
Type:&proto.Provision_Response_Complete{
38+
Complete:&proto.Provision_Complete{
39+
Resources: []*proto.Resource{{
40+
Name:"example",
41+
Type:"aws_instance",
42+
Agents: []*proto.Agent{{
43+
Id:uuid.NewString(),
4344
Name:"example",
44-
Type:"aws_instance",
45-
Agents: []*proto.Agent{{
46-
Id:uuid.NewString(),
47-
Name:"example",
48-
}},
4945
}},
50-
},
46+
}},
5147
},
52-
}},
53-
Provision: []*proto.Provision_Response{{
54-
Type:&proto.Provision_Response_Complete{
55-
Complete:&proto.Provision_Complete{
56-
Resources: []*proto.Resource{{
48+
},
49+
}},
50+
Provision: []*proto.Provision_Response{{
51+
Type:&proto.Provision_Response_Complete{
52+
Complete:&proto.Provision_Complete{
53+
Resources: []*proto.Resource{{
54+
Name:"example",
55+
Type:"aws_instance",
56+
Agents: []*proto.Agent{{
57+
Id:uuid.NewString(),
5758
Name:"example",
58-
Type:"aws_instance",
59-
Agents: []*proto.Agent{{
60-
Id:uuid.NewString(),
61-
Name:"example",
62-
Auth:&proto.Agent_Token{
63-
Token:authToken,
64-
},
65-
}},
59+
Auth:&proto.Agent_Token{
60+
Token:authToken,
61+
},
6662
}},
67-
},
63+
}},
6864
},
69-
}},
70-
})
71-
coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
72-
template:=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID)
73-
workspace:=coderdtest.CreateWorkspace(t,client,codersdk.Me,template.ID)
74-
coderdtest.AwaitWorkspaceBuildJob(t,client,workspace.LatestBuild.ID)
75-
agentClient:=codersdk.New(client.URL)
76-
agentClient.SessionToken=authToken
77-
agentCloser:=agent.New(agentClient.ListenWorkspaceAgent,&peer.ConnOptions{
78-
Logger:slogtest.Make(t,nil),
79-
})
80-
t.Cleanup(func() {
81-
_=agentCloser.Close()
82-
})
83-
tempFile,err:=os.CreateTemp(t.TempDir(),"")
84-
require.NoError(t,err)
85-
_=tempFile.Close()
86-
cmd,root:=clitest.New(t,"config-ssh","--binary-file",binPath,"--ssh-config-file",tempFile.Name())
87-
clitest.SetupConfig(t,client,root)
88-
doneChan:=make(chanstruct{})
89-
pty:=ptytest.New(t)
90-
cmd.SetIn(pty.Input())
91-
cmd.SetOut(pty.Output())
92-
gofunc() {
93-
deferclose(doneChan)
94-
err:=cmd.Execute()
65+
},
66+
}},
67+
})
68+
coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
69+
template:=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID)
70+
workspace:=coderdtest.CreateWorkspace(t,client,codersdk.Me,template.ID)
71+
coderdtest.AwaitWorkspaceBuildJob(t,client,workspace.LatestBuild.ID)
72+
agentClient:=codersdk.New(client.URL)
73+
agentClient.SessionToken=authToken
74+
agentCloser:=agent.New(agentClient.ListenWorkspaceAgent,&peer.ConnOptions{
75+
Logger:slogtest.Make(t,nil),
76+
})
77+
t.Cleanup(func() {
78+
_=agentCloser.Close()
79+
})
80+
tempFile,err:=os.CreateTemp(t.TempDir(),"")
81+
require.NoError(t,err)
82+
_=tempFile.Close()
83+
resources:=coderdtest.AwaitWorkspaceAgents(t,client,workspace.LatestBuild.ID)
84+
agentConn,err:=client.DialWorkspaceAgent(context.Background(),resources[0].Agents[0].ID,nil,nil)
85+
require.NoError(t,err)
86+
deferagentConn.Close()
87+
88+
// Using socat we can force SSH to use a UNIX socket
89+
// created in this test. That way we still validate
90+
// our configuration, but use the native SSH command
91+
// line to interface.
92+
socket:=filepath.Join(t.TempDir(),"ssh")
93+
listener,err:=net.Listen("unix",socket)
94+
require.NoError(t,err)
95+
gofunc() {
96+
for {
97+
conn,err:=listener.Accept()
98+
iferr!=nil {
99+
return
100+
}
101+
ssh,err:=agentConn.SSH()
95102
require.NoError(t,err)
96-
}()
97-
<-doneChan
98-
t.Log(tempFile.Name())
99-
time.Sleep(time.Hour)
100-
output,err:=exec.Command("ssh","-F",tempFile.Name(),"coder."+workspace.Name,"echo","test").Output()
101-
t.Log(string(output))
102-
require.NoError(t,err)
103-
require.Equal(t,"test",strings.TrimSpace(string(output)))
103+
goio.Copy(conn,ssh)
104+
goio.Copy(ssh,conn)
105+
}
106+
}()
107+
t.Cleanup(func() {
108+
_=listener.Close()
104109
})
110+
111+
cmd,root:=clitest.New(t,"config-ssh","--ssh-option","ProxyCommand socat - UNIX-CLIENT:"+socket,"--ssh-config-file",tempFile.Name())
112+
clitest.SetupConfig(t,client,root)
113+
doneChan:=make(chanstruct{})
114+
pty:=ptytest.New(t)
115+
cmd.SetIn(pty.Input())
116+
cmd.SetOut(pty.Output())
117+
gofunc() {
118+
deferclose(doneChan)
119+
err:=cmd.Execute()
120+
require.NoError(t,err)
121+
}()
122+
<-doneChan
123+
124+
t.Log(tempFile.Name())
125+
// #nosec
126+
data,err:=exec.Command("ssh","-F",tempFile.Name(),"coder."+workspace.Name,"echo","test").Output()
127+
require.NoError(t,err)
128+
require.Equal(t,"test",strings.TrimSpace(string(data)))
105129
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp