Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 41fddac

Browse files
committed
add integration test
1 parent 234fc25 commit 41fddac

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed

agent/agent_test.go

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

0 commit comments

Comments
 (0)