@@ -25,24 +25,28 @@ import (
25
25
"testing"
26
26
"time"
27
27
28
+ "go.uber.org/goleak"
29
+ "tailscale.com/net/speedtest"
30
+ "tailscale.com/tailcfg"
31
+
28
32
"github.com/bramvdbogaerde/go-scp"
29
33
"github.com/google/uuid"
34
+ "github.com/ory/dockertest/v3"
35
+ "github.com/ory/dockertest/v3/docker"
30
36
"github.com/pion/udp"
31
37
"github.com/pkg/sftp"
32
38
"github.com/prometheus/client_golang/prometheus"
33
39
promgo "github.com/prometheus/client_model/go"
34
40
"github.com/spf13/afero"
35
41
"github.com/stretchr/testify/assert"
36
42
"github.com/stretchr/testify/require"
37
- "go.uber.org/goleak"
38
43
"golang.org/x/crypto/ssh"
39
44
"golang.org/x/exp/slices"
40
45
"golang.org/x/xerrors"
41
- "tailscale.com/net/speedtest"
42
- "tailscale.com/tailcfg"
43
46
44
47
"cdr.dev/slog"
45
48
"cdr.dev/slog/sloggers/slogtest"
49
+
46
50
"github.com/coder/coder/v2/agent"
47
51
"github.com/coder/coder/v2/agent/agentssh"
48
52
"github.com/coder/coder/v2/agent/agenttest"
@@ -1761,6 +1765,69 @@ func TestAgent_ReconnectingPTY(t *testing.T) {
1761
1765
}
1762
1766
}
1763
1767
1768
+ // This tests end-to-end functionality of connecting to a running container
1769
+ // and executing a command. It creates a real Docker container and runs a
1770
+ // command. As such, it does not run by default in CI.
1771
+ // You can run it manually as follows:
1772
+ //
1773
+ // CODER_TEST_USE_DOCKER=1 go test -count=1 ./agent -run TestAgent_ReconnectingPTYContainer
1774
+ func TestAgent_ReconnectingPTYContainer (t * testing.T ) {
1775
+ t .Parallel ()
1776
+ if ctud , ok := os .LookupEnv ("CODER_TEST_USE_DOCKER" ); ! ok || ctud != "1" {
1777
+ t .Skip ("Set CODER_TEST_USE_DOCKER=1 to run this test" )
1778
+ }
1779
+
1780
+ ctx , cancel := context .WithTimeout (context .Background (), testutil .WaitLong )
1781
+ defer cancel ()
1782
+
1783
+ pool , err := dockertest .NewPool ("" )
1784
+ require .NoError (t , err , "Could not connect to docker" )
1785
+ ct , err := pool .RunWithOptions (& dockertest.RunOptions {
1786
+ Repository : "busybox" ,
1787
+ Tag : "latest" ,
1788
+ Cmd : []string {"sleep" , "infnity" },
1789
+ }, func (config * docker.HostConfig ) {
1790
+ config .AutoRemove = true
1791
+ config .RestartPolicy = docker.RestartPolicy {Name : "no" }
1792
+ })
1793
+ require .NoError (t , err , "Could not start container" )
1794
+ // Wait for container to start
1795
+ require .Eventually (t , func () bool {
1796
+ ct , ok := pool .ContainerByName (ct .Container .Name )
1797
+ return ok && ct .Container .State .Running
1798
+ }, testutil .WaitShort , testutil .IntervalSlow , "Container did not start in time" )
1799
+
1800
+ // nolint: dogsled
1801
+ conn , _ , _ , _ , _ := setupAgent (t , agentsdk.Manifest {}, 0 )
1802
+ ac , err := conn .ReconnectingPTY (ctx , uuid .New (), 80 , 80 , "/bin/sh" , func (arp * workspacesdk.AgentReconnectingPTYInit ) {
1803
+ arp .Container = ct .Container .ID
1804
+ })
1805
+ require .NoError (t , err , "failed to create ReconnectingPTY" )
1806
+ defer ac .Close ()
1807
+ tr := testutil .NewTerminalReader (t , ac )
1808
+
1809
+ require .NoError (t , tr .ReadUntil (ctx , func (line string ) bool {
1810
+ return strings .Contains (line , "#" ) || strings .Contains (line , "$" )
1811
+ }), "find prompt" )
1812
+
1813
+ require .NoError (t , json .NewEncoder (ac ).Encode (workspacesdk.ReconnectingPTYRequest {
1814
+ Data : "hostname\r " ,
1815
+ }), "write hostname" )
1816
+ require .NoError (t , tr .ReadUntil (ctx , func (line string ) bool {
1817
+ return strings .Contains (line , "hostname" )
1818
+ }), "find hostname command" )
1819
+
1820
+ require .NoError (t , tr .ReadUntil (ctx , func (line string ) bool {
1821
+ return strings .Contains (line , ct .Container .Config .Hostname )
1822
+ }), "find hostname output" )
1823
+ require .NoError (t , json .NewEncoder (ac ).Encode (workspacesdk.ReconnectingPTYRequest {
1824
+ Data : "exit\r " ,
1825
+ }), "write exit command" )
1826
+
1827
+ // Wait for the connection to close.
1828
+ require .ErrorIs (t , tr .ReadUntil (ctx , nil ), io .EOF )
1829
+ }
1830
+
1764
1831
func TestAgent_Dial (t * testing.T ) {
1765
1832
t .Parallel ()
1766
1833
0 commit comments