mirror of
https://github.com/coder/coder.git
synced 2026-06-03 13:08:25 +00:00
f41275eb39
Relates to https://github.com/coder/internal/issues/711 This PR implements a project discovery mechanism that searches for any dev container projects and makes them visible in the UI so that they can be started. To make the wording on the site more clear, "Rebuild" has been changed to "Start" when there is no container associated with a known dev container configuration. I've also made it so that site will show the dev container config path when there is no other name available. ### Design decisions Just want to ensure my explanation for a few design decisions are noted down: - We only search for dev container configurations inside git repositories - We only search for these git repositories if they're at the top level or a direct child of the agent directory. This limited approach is to reduce the amount of files we ultimately walk when trying to find these projects. It makes sense to limit it to only the agent directory, although I'm open to expanding how deep we search.
143 lines
4.0 KiB
Go
143 lines
4.0 KiB
Go
package cli_test
|
|
|
|
import (
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/ory/dockertest/v3"
|
|
"github.com/ory/dockertest/v3/docker"
|
|
|
|
"github.com/coder/coder/v2/agent"
|
|
"github.com/coder/coder/v2/agent/agentcontainers"
|
|
"github.com/coder/coder/v2/agent/agenttest"
|
|
"github.com/coder/coder/v2/cli/clitest"
|
|
"github.com/coder/coder/v2/coderd/coderdtest"
|
|
"github.com/coder/coder/v2/pty/ptytest"
|
|
"github.com/coder/coder/v2/testutil"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestExpRpty(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("DefaultCommand", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client, workspace, agentToken := setupWorkspaceForAgent(t)
|
|
inv, root := clitest.New(t, "exp", "rpty", workspace.Name)
|
|
clitest.SetupConfig(t, client, root)
|
|
pty := ptytest.New(t).Attach(inv)
|
|
|
|
ctx := testutil.Context(t, testutil.WaitLong)
|
|
|
|
_ = agenttest.New(t, client.URL, agentToken)
|
|
_ = coderdtest.NewWorkspaceAgentWaiter(t, client, workspace.ID).Wait()
|
|
|
|
cmdDone := tGo(t, func() {
|
|
err := inv.WithContext(ctx).Run()
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
pty.WriteLine("exit")
|
|
<-cmdDone
|
|
})
|
|
|
|
t.Run("Command", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client, workspace, agentToken := setupWorkspaceForAgent(t)
|
|
randStr := uuid.NewString()
|
|
inv, root := clitest.New(t, "exp", "rpty", workspace.Name, "echo", randStr)
|
|
clitest.SetupConfig(t, client, root)
|
|
pty := ptytest.New(t).Attach(inv)
|
|
|
|
ctx := testutil.Context(t, testutil.WaitLong)
|
|
|
|
_ = agenttest.New(t, client.URL, agentToken)
|
|
_ = coderdtest.NewWorkspaceAgentWaiter(t, client, workspace.ID).Wait()
|
|
|
|
cmdDone := tGo(t, func() {
|
|
err := inv.WithContext(ctx).Run()
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
pty.ExpectMatch(randStr)
|
|
<-cmdDone
|
|
})
|
|
|
|
t.Run("NotFound", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client, _, _ := setupWorkspaceForAgent(t)
|
|
inv, root := clitest.New(t, "exp", "rpty", "not-found")
|
|
clitest.SetupConfig(t, client, root)
|
|
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
err := inv.WithContext(ctx).Run()
|
|
require.ErrorContains(t, err, "not found")
|
|
})
|
|
|
|
t.Run("Container", func(t *testing.T) {
|
|
t.Parallel()
|
|
// Skip this test on non-Linux platforms since it requires Docker
|
|
if runtime.GOOS != "linux" {
|
|
t.Skip("Skipping test on non-Linux platform")
|
|
}
|
|
|
|
wantLabel := "coder.devcontainers.TestExpRpty.Container"
|
|
|
|
client, workspace, agentToken := setupWorkspaceForAgent(t)
|
|
ctx := testutil.Context(t, testutil.WaitLong)
|
|
pool, err := dockertest.NewPool("")
|
|
require.NoError(t, err, "Could not connect to docker")
|
|
ct, err := pool.RunWithOptions(&dockertest.RunOptions{
|
|
Repository: "busybox",
|
|
Tag: "latest",
|
|
Cmd: []string{"sleep", "infinity"},
|
|
Labels: map[string]string{
|
|
wantLabel: "true",
|
|
},
|
|
}, func(config *docker.HostConfig) {
|
|
config.AutoRemove = true
|
|
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
|
|
})
|
|
require.NoError(t, err, "Could not start container")
|
|
// Wait for container to start
|
|
require.Eventually(t, func() bool {
|
|
ct, ok := pool.ContainerByName(ct.Container.Name)
|
|
return ok && ct.Container.State.Running
|
|
}, testutil.WaitShort, testutil.IntervalSlow, "Container did not start in time")
|
|
t.Cleanup(func() {
|
|
err := pool.Purge(ct)
|
|
require.NoError(t, err, "Could not stop container")
|
|
})
|
|
|
|
_ = agenttest.New(t, client.URL, agentToken, func(o *agent.Options) {
|
|
o.Devcontainers = true
|
|
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
|
agentcontainers.WithProjectDiscovery(false),
|
|
agentcontainers.WithContainerLabelIncludeFilter(wantLabel, "true"),
|
|
)
|
|
})
|
|
_ = coderdtest.NewWorkspaceAgentWaiter(t, client, workspace.ID).Wait()
|
|
|
|
inv, root := clitest.New(t, "exp", "rpty", workspace.Name, "-c", ct.Container.ID)
|
|
clitest.SetupConfig(t, client, root)
|
|
pty := ptytest.New(t).Attach(inv)
|
|
|
|
cmdDone := tGo(t, func() {
|
|
err := inv.WithContext(ctx).Run()
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
pty.ExpectMatch(" #")
|
|
pty.WriteLine("hostname")
|
|
pty.ExpectMatch(ct.Container.Config.Hostname)
|
|
pty.WriteLine("exit")
|
|
<-cmdDone
|
|
})
|
|
}
|