e2e: robustness changes for ddev test

The most important change here is to ensure that the correct Compose
standalone binary is used by `ddev`. Since it invokes Compose itself,
we need to ensure that `PATH` is set appropriately such that it finds
the binary we want to test rather than something from the system.

As part of this, the rest of the environment has been isolated, which
should make the test more reliable, and avoids polluting `~/.ddev`
with test artifacts by using a tmpdir as `HOME` for the test instead
of the user's real home folder.

Signed-off-by: Milas Bowman <milas.bowman@docker.com>
This commit is contained in:
Milas Bowman 2022-06-16 08:44:18 -04:00
parent ea8341865d
commit 1c41df8f56
2 changed files with 79 additions and 35 deletions

View File

@ -19,11 +19,13 @@ package e2e
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/stretchr/testify/require"
"gotest.tools/v3/assert"
)
@ -31,26 +33,38 @@ const ddevVersion = "v1.19.1"
func TestComposeRunDdev(t *testing.T) {
if !composeStandaloneMode {
t.Skip("Not running on standalone mode.")
t.Skip("Not running in plugin mode - ddev only supports invoking standalone `docker-compose`")
}
if runtime.GOOS == "windows" {
t.Skip("Running on Windows. Skipping...")
}
_ = os.Setenv("DDEV_DEBUG", "true")
c := NewParallelCLI(t)
dir, err := os.MkdirTemp("", t.Name()+"-")
assert.NilError(t, err)
// ddev shells out to `docker` and `docker-compose` (standalone), so a
// temporary directory is created with symlinks to system Docker and the
// locally-built standalone Compose binary to use as PATH
pathDir := t.TempDir()
dockerBin, err := exec.LookPath(DockerExecutableName)
require.NoError(t, err, "Could not find %q in path", DockerExecutableName)
require.NoError(t, os.Symlink(dockerBin, filepath.Join(pathDir, DockerExecutableName)),
"Could not create %q symlink", DockerExecutableName)
// ddev needs to be able to find mkcert to figure out where certs are.
_ = os.Setenv("PATH", fmt.Sprintf("%s:%s", os.Getenv("PATH"), dir))
composeBin := ComposeStandalonePath(t)
require.NoError(t, os.Symlink(composeBin, filepath.Join(pathDir, DockerComposeExecutableName)),
"Could not create %q symlink", DockerComposeExecutableName)
siteName := filepath.Base(dir)
c := NewCLI(t, WithEnv(
"DDEV_DEBUG=true",
fmt.Sprintf("HOME=%s", t.TempDir()),
fmt.Sprintf("USER=%s", os.Getenv("USER")),
fmt.Sprintf("PATH=%s", pathDir),
))
ddevDir := t.TempDir()
siteName := filepath.Base(ddevDir)
t.Cleanup(func() {
_ = c.RunCmdInDir(t, dir, "./ddev", "delete", "-Oy")
_ = c.RunCmdInDir(t, dir, "./ddev", "poweroff")
_ = os.RemoveAll(dir)
_ = c.RunCmdInDir(t, ddevDir, "./ddev", "delete", "-Oy")
_ = c.RunCmdInDir(t, ddevDir, "./ddev", "poweroff")
})
osName := "linux"
@ -59,27 +73,26 @@ func TestComposeRunDdev(t *testing.T) {
}
compressedFilename := fmt.Sprintf("ddev_%s-%s.%s.tar.gz", osName, runtime.GOARCH, ddevVersion)
c.RunCmdInDir(t, dir, "curl", "-LO",
fmt.Sprintf("https://github.com/drud/ddev/releases/download/%s/%s",
ddevVersion,
compressedFilename))
c.RunCmdInDir(t, ddevDir, "curl", "-LO", fmt.Sprintf("https://github.com/drud/ddev/releases/download/%s/%s",
ddevVersion,
compressedFilename))
c.RunCmdInDir(t, dir, "tar", "-xzf", compressedFilename)
c.RunCmdInDir(t, ddevDir, "tar", "-xzf", compressedFilename)
// Create a simple index.php we can test against.
c.RunCmdInDir(t, dir, "sh", "-c", "echo '<?php\nprint \"ddev is working\";' >index.php")
c.RunCmdInDir(t, ddevDir, "sh", "-c", "echo '<?php\nprint \"ddev is working\";' >index.php")
c.RunCmdInDir(t, dir, "./ddev", "config", "--auto")
c.RunCmdInDir(t, dir, "./ddev", "config", "global", "--use-docker-compose-from-path")
vRes := c.RunCmdInDir(t, dir, "./ddev", "version")
c.RunCmdInDir(t, ddevDir, "./ddev", "config", "--auto")
c.RunCmdInDir(t, ddevDir, "./ddev", "config", "global", "--use-docker-compose-from-path")
vRes := c.RunCmdInDir(t, ddevDir, "./ddev", "version")
out := vRes.Stdout()
fmt.Printf("ddev version: %s\n", out)
c.RunCmdInDir(t, dir, "./ddev", "poweroff")
c.RunCmdInDir(t, ddevDir, "./ddev", "poweroff")
c.RunCmdInDir(t, dir, "./ddev", "start", "-y")
c.RunCmdInDir(t, ddevDir, "./ddev", "start", "-y")
curlRes := c.RunCmdInDir(t, dir, "curl", "-sSL", fmt.Sprintf("http://%s.ddev.site", siteName))
curlRes := c.RunCmdInDir(t, ddevDir, "curl", "-sSL", fmt.Sprintf("http://%s.ddev.site", siteName))
out = curlRes.Stdout()
fmt.Println(out)
assert.Assert(t, strings.Contains(out, "ddev is working"), "Could not start project")

View File

@ -32,7 +32,6 @@ import (
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/icmd"
"gotest.tools/v3/poll"
@ -61,18 +60,50 @@ func init() {
// CLI is used to wrap the CLI for end to end testing
type CLI struct {
ConfigDir string
env []string
}
// NewParallelCLI returns a configured CLI with t.Parallel() set
func NewParallelCLI(t *testing.T) *CLI {
// CLIOption to customize behavior for all commands for a CLI instance.
type CLIOption func(c *CLI)
// NewParallelCLI marks the parent test as parallel and returns a CLI instance
// suitable for usage across child tests.
func NewParallelCLI(t *testing.T, opts ...CLIOption) *CLI {
t.Helper()
t.Parallel()
return NewCLI(t)
return NewCLI(t, opts...)
}
// NewCLI returns a CLI to use for E2E tests
func NewCLI(t testing.TB) *CLI {
d, err := ioutil.TempDir("", "")
assert.Check(t, is.Nil(err))
// NewCLI creates a CLI instance for running E2E tests.
func NewCLI(t testing.TB, opts ...CLIOption) *CLI {
t.Helper()
configDir := t.TempDir()
initializePlugins(t, configDir)
c := &CLI{
ConfigDir: configDir,
}
for _, opt := range opts {
opt(c)
}
return c
}
// WithEnv sets environment variables that will be passed to commands.
func WithEnv(env ...string) CLIOption {
return func(c *CLI) {
c.env = append(c.env, env...)
}
}
// initializePlugins copies the necessary plugin files to the temporary config
// directory for the test.
func initializePlugins(t testing.TB, d string) {
t.Helper()
t.Cleanup(func() {
if t.Failed() {
@ -102,8 +133,6 @@ func NewCLI(t testing.TB) *CLI {
panic(err)
}
}
return &CLI{ConfigDir: d}
}
func dirContents(dir string) []string {
@ -168,13 +197,15 @@ func (c *CLI) BaseEnvironment() []string {
func (c *CLI) NewCmd(command string, args ...string) icmd.Cmd {
return icmd.Cmd{
Command: append([]string{command}, args...),
Env: c.BaseEnvironment(),
Env: append(c.BaseEnvironment(), c.env...),
}
}
// NewCmdWithEnv creates a cmd object configured with the test environment set with additional env vars
func (c *CLI) NewCmdWithEnv(envvars []string, command string, args ...string) icmd.Cmd {
cmdEnv := append(c.BaseEnvironment(), envvars...)
// base env -> CLI overrides -> cmd overrides
cmdEnv := append(c.BaseEnvironment(), c.env...)
cmdEnv = append(cmdEnv, envvars...)
return icmd.Cmd{
Command: append([]string{command}, args...),
Env: cmdEnv,