1
1
package cli_test
2
2
3
3
import (
4
+ "context"
5
+ "io"
6
+ "net"
4
7
"os"
5
8
"os/exec"
6
9
"path/filepath"
7
10
"strings"
8
11
"testing"
9
- "time"
10
12
11
- "cdr.dev/slog/sloggers/slogtest"
12
13
"github.com/google/uuid"
13
14
"github.com/stretchr/testify/require"
14
15
16
+ "cdr.dev/slog/sloggers/slogtest"
17
+
15
18
"github.com/coder/coder/agent"
16
19
"github.com/coder/coder/cli/clitest"
17
20
"github.com/coder/coder/coderd/coderdtest"
@@ -24,82 +27,103 @@ import (
24
27
25
28
func TestConfigSSH (t * testing.T ) {
26
29
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 (),
43
44
Name :"example" ,
44
- Type :"aws_instance" ,
45
- Agents : []* proto.Agent {{
46
- Id :uuid .NewString (),
47
- Name :"example" ,
48
- }},
49
45
}},
50
- },
46
+ }} ,
51
47
},
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 (),
57
58
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
+ },
66
62
}},
67
- },
63
+ }} ,
68
64
},
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 (chan struct {})
89
- pty := ptytest .New (t )
90
- cmd .SetIn (pty .Input ())
91
- cmd .SetOut (pty .Output ())
92
- go func () {
93
- defer close (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
+ defer agentConn .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
+ go func () {
96
+ for {
97
+ conn ,err := listener .Accept ()
98
+ if err != nil {
99
+ return
100
+ }
101
+ ssh ,err := agentConn .SSH ()
95
102
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
+ go io .Copy (conn ,ssh )
104
+ go io .Copy (ssh ,conn )
105
+ }
106
+ }()
107
+ t .Cleanup (func () {
108
+ _ = listener .Close ()
104
109
})
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 (chan struct {})
114
+ pty := ptytest .New (t )
115
+ cmd .SetIn (pty .Input ())
116
+ cmd .SetOut (pty .Output ())
117
+ go func () {
118
+ defer close (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 )))
105
129
}