mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
127 lines
4.8 KiB
Go
127 lines
4.8 KiB
Go
package agentcontainers_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/ory/dockertest/v3"
|
|
"github.com/ory/dockertest/v3/docker"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/coder/coder/v2/agent/agentcontainers"
|
|
"github.com/coder/coder/v2/agent/agentexec"
|
|
"github.com/coder/coder/v2/testutil"
|
|
)
|
|
|
|
// TestIntegrationDockerCLI tests the DetectArchitecture, Copy, and
|
|
// ExecAs methods using a real Docker container. All tests share a
|
|
// single container to avoid setup overhead.
|
|
//
|
|
// Run manually with: CODER_TEST_USE_DOCKER=1 go test ./agent/agentcontainers -run TestIntegrationDockerCLI
|
|
//
|
|
//nolint:tparallel,paralleltest // Docker integration tests don't run in parallel to avoid flakiness.
|
|
func TestIntegrationDockerCLI(t *testing.T) {
|
|
if ctud, ok := os.LookupEnv("CODER_TEST_USE_DOCKER"); !ok || ctud != "1" {
|
|
t.Skip("Set CODER_TEST_USE_DOCKER=1 to run this test")
|
|
}
|
|
|
|
pool, err := dockertest.NewPool("")
|
|
require.NoError(t, err, "Could not connect to docker")
|
|
|
|
// Start a simple busybox container for all subtests to share.
|
|
ct, err := pool.RunWithOptions(&dockertest.RunOptions{
|
|
Repository: "busybox",
|
|
Tag: "latest",
|
|
Cmd: []string{"sleep", "infinity"},
|
|
}, func(config *docker.HostConfig) {
|
|
config.AutoRemove = true
|
|
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
|
|
})
|
|
require.NoError(t, err, "Could not start test docker container")
|
|
t.Logf("Created container %q", ct.Container.Name)
|
|
t.Cleanup(func() {
|
|
assert.NoError(t, pool.Purge(ct), "Could not purge resource %q", ct.Container.Name)
|
|
t.Logf("Purged container %q", ct.Container.Name)
|
|
})
|
|
|
|
// 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")
|
|
|
|
dcli := agentcontainers.NewDockerCLI(agentexec.DefaultExecer)
|
|
ctx := testutil.Context(t, testutil.WaitMedium) // Longer timeout for multiple subtests
|
|
containerName := strings.TrimPrefix(ct.Container.Name, "/")
|
|
|
|
t.Run("DetectArchitecture", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
arch, err := dcli.DetectArchitecture(ctx, containerName)
|
|
require.NoError(t, err, "DetectArchitecture failed")
|
|
require.NotEmpty(t, arch, "arch has no content")
|
|
require.Equal(t, runtime.GOARCH, arch, "architecture does not match runtime, did you run this test with a remote Docker socket?")
|
|
|
|
t.Logf("Detected architecture: %s", arch)
|
|
})
|
|
|
|
t.Run("Copy", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
want := "Help, I'm trapped!"
|
|
tempFile := filepath.Join(t.TempDir(), "test-file.txt")
|
|
err := os.WriteFile(tempFile, []byte(want), 0o600)
|
|
require.NoError(t, err, "create test file failed")
|
|
|
|
destPath := "/tmp/copied-file.txt"
|
|
err = dcli.Copy(ctx, containerName, tempFile, destPath)
|
|
require.NoError(t, err, "Copy failed")
|
|
|
|
got, err := dcli.ExecAs(ctx, containerName, "", "cat", destPath)
|
|
require.NoError(t, err, "ExecAs failed after Copy")
|
|
require.Equal(t, want, string(got), "copied file content did not match original")
|
|
|
|
t.Logf("Successfully copied file from %s to container %s:%s", tempFile, containerName, destPath)
|
|
})
|
|
|
|
t.Run("ExecAs", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Test ExecAs without specifying user (should use container's default).
|
|
want := "root"
|
|
got, err := dcli.ExecAs(ctx, containerName, "", "whoami")
|
|
require.NoError(t, err, "ExecAs without user should succeed")
|
|
require.Equal(t, want, string(got), "ExecAs without user should output expected string")
|
|
|
|
// Test ExecAs with numeric UID (non root).
|
|
want = "1000"
|
|
_, err = dcli.ExecAs(ctx, containerName, want, "whoami")
|
|
require.Error(t, err, "ExecAs with UID 1000 should fail as user does not exist in busybox")
|
|
require.Contains(t, err.Error(), "whoami: unknown uid 1000", "ExecAs with UID 1000 should return 'unknown uid' error")
|
|
|
|
// Test ExecAs with root user (should convert "root" to "0", which still outputs root due to passwd).
|
|
want = "root"
|
|
got, err = dcli.ExecAs(ctx, containerName, "root", "whoami")
|
|
require.NoError(t, err, "ExecAs with root user should succeed")
|
|
require.Equal(t, want, string(got), "ExecAs with root user should output expected string")
|
|
|
|
// Test ExecAs with numeric UID.
|
|
want = "root"
|
|
got, err = dcli.ExecAs(ctx, containerName, "0", "whoami")
|
|
require.NoError(t, err, "ExecAs with UID 0 should succeed")
|
|
require.Equal(t, want, string(got), "ExecAs with UID 0 should output expected string")
|
|
|
|
// Test ExecAs with multiple arguments.
|
|
want = "multiple args test"
|
|
got, err = dcli.ExecAs(ctx, containerName, "", "sh", "-c", "echo '"+want+"'")
|
|
require.NoError(t, err, "ExecAs with multiple arguments should succeed")
|
|
require.Equal(t, want, string(got), "ExecAs with multiple arguments should output expected string")
|
|
|
|
t.Logf("Successfully executed commands in container %s", containerName)
|
|
})
|
|
}
|