Skip to content

Commit 2d93ee3

Browse files
committed
add integration test
1 parent 234fc25 commit 2d93ee3

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

agent/agent_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,6 +1937,134 @@ func TestAgent_ReconnectingPTYContainer(t *testing.T) {
19371937
require.ErrorIs(t, tr.ReadUntil(ctx, nil), io.EOF)
19381938
}
19391939

1940+
// This tests end-to-end functionality of auto-starting a devcontainer.
1941+
// It runs "devcontainer up" which creates a real Docker container. As
1942+
// such, it does not run by default in CI.
1943+
//
1944+
// You can run it manually as follows:
1945+
//
1946+
// CODER_TEST_USE_DOCKER=1 go test -count=1 ./agent -run TestAgent_DevcontainerAutostart
1947+
func TestAgent_DevcontainerAutostart(t *testing.T) {
1948+
t.Parallel()
1949+
if os.Getenv("CODER_TEST_USE_DOCKER") != "1" {
1950+
t.Skip("Set CODER_TEST_USE_DOCKER=1 to run this test")
1951+
}
1952+
1953+
ctx := testutil.Context(t, testutil.WaitLong)
1954+
1955+
// Connect to Docker
1956+
pool, err := dockertest.NewPool("")
1957+
require.NoError(t, err, "Could not connect to docker")
1958+
1959+
// Prepare temporary devcontainer for test (mywork).
1960+
devcontainerID := uuid.New()
1961+
tempWorkspaceFolder := t.TempDir()
1962+
tempWorkspaceFolder = filepath.Join(tempWorkspaceFolder, "mywork")
1963+
t.Logf("Workspace folder: %s", tempWorkspaceFolder)
1964+
devcontainerPath := filepath.Join(tempWorkspaceFolder, ".devcontainer")
1965+
err = os.MkdirAll(devcontainerPath, 0o755)
1966+
require.NoError(t, err, "create devcontainer directory")
1967+
devcontainerFile := filepath.Join(devcontainerPath, "devcontainer.json")
1968+
err = os.WriteFile(devcontainerFile, []byte(`{
1969+
"name": "mywork",
1970+
"image": "busybox:latest",
1971+
"cmd": ["sleep", "infinity"]
1972+
}`), 0o600)
1973+
require.NoError(t, err, "write devcontainer.json")
1974+
1975+
manifest := agentsdk.Manifest{
1976+
// Set up pre-conditions for auto-starting a devcontainer, the script
1977+
// is expected to be prepared by the provisioner normally.
1978+
Devcontainers: []codersdk.WorkspaceAgentDevcontainer{
1979+
{
1980+
ID: devcontainerID,
1981+
Name: "test",
1982+
WorkspaceFolder: tempWorkspaceFolder,
1983+
},
1984+
},
1985+
Scripts: []codersdk.WorkspaceAgentScript{
1986+
{
1987+
ID: devcontainerID,
1988+
LogSourceID: agentsdk.ExternalLogSourceID,
1989+
RunOnStart: true,
1990+
Script: "echo this-will-be-replaced",
1991+
DisplayName: "Dev Container (test)",
1992+
},
1993+
},
1994+
}
1995+
// nolint: dogsled
1996+
conn, _, _, _, _ := setupAgent(t, manifest, 0, func(_ *agenttest.Client, o *agent.Options) {
1997+
o.ExperimentalDevcontainersEnabled = true
1998+
})
1999+
2000+
t.Logf("Waiting for container with label: devcontainer.local_folder=%s", tempWorkspaceFolder)
2001+
2002+
var container docker.APIContainers
2003+
require.Eventually(t, func() bool {
2004+
containers, err := pool.Client.ListContainers(docker.ListContainersOptions{All: true})
2005+
if err != nil {
2006+
t.Logf("Error listing containers: %v", err)
2007+
return false
2008+
}
2009+
2010+
for _, c := range containers {
2011+
t.Logf("Found container: %s with labels: %v", c.ID[:12], c.Labels)
2012+
if labelValue, ok := c.Labels["devcontainer.local_folder"]; ok {
2013+
if labelValue == tempWorkspaceFolder {
2014+
t.Logf("Found matching container: %s", c.ID[:12])
2015+
container = c
2016+
return true
2017+
}
2018+
}
2019+
}
2020+
2021+
return false
2022+
}, testutil.WaitSuperLong, testutil.IntervalMedium, "no container with workspace folder label found")
2023+
2024+
t.Cleanup(func() {
2025+
// We can't rely on pool here because the container is not
2026+
// managed by it (it is managed by @devcontainer/cli).
2027+
err := pool.Client.RemoveContainer(docker.RemoveContainerOptions{
2028+
ID: container.ID,
2029+
RemoveVolumes: true,
2030+
Force: true,
2031+
})
2032+
assert.NoError(t, err, "remove container")
2033+
})
2034+
2035+
containerInfo, err := pool.Client.InspectContainer(container.ID)
2036+
require.NoError(t, err, "inspect container")
2037+
t.Logf("Container state: status: %v", containerInfo.State.Status)
2038+
require.True(t, containerInfo.State.Running, "container should be running")
2039+
2040+
ac, err := conn.ReconnectingPTY(ctx, uuid.New(), 80, 80, "", func(opts *workspacesdk.AgentReconnectingPTYInit) {
2041+
opts.Container = container.ID
2042+
})
2043+
require.NoError(t, err, "failed to create ReconnectingPTY")
2044+
defer ac.Close()
2045+
2046+
// Use terminal reader so we can see output in case somethin goes wrong.
2047+
tr := testutil.NewTerminalReader(t, ac)
2048+
2049+
require.NoError(t, tr.ReadUntil(ctx, func(line string) bool {
2050+
return strings.Contains(line, "#") || strings.Contains(line, "$")
2051+
}), "find prompt")
2052+
2053+
wantFileName := "file-from-devcontainer"
2054+
wantFile := filepath.Join(tempWorkspaceFolder, wantFileName)
2055+
2056+
require.NoError(t, json.NewEncoder(ac).Encode(workspacesdk.ReconnectingPTYRequest{
2057+
// NOTE(mafredri): We must use absolute path here for some reason.
2058+
Data: fmt.Sprintf("touch /workspaces/mywork/%s; exit\r", wantFileName),
2059+
}), "create file inside devcontainer")
2060+
2061+
// Wait for the connection to close to ensure the touch was executed.
2062+
require.ErrorIs(t, tr.ReadUntil(ctx, nil), io.EOF)
2063+
2064+
_, err = os.Stat(wantFile)
2065+
require.NoError(t, err, "file should exist outside devcontainer")
2066+
}
2067+
19402068
func TestAgent_Dial(t *testing.T) {
19412069
t.Parallel()
19422070

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy