test: add basic integration tests

Signed-off-by: Justin Chadwell <me@jedevc.com>
This commit is contained in:
Justin Chadwell
2023-05-15 18:48:58 +01:00
parent e61a8cf637
commit 2d124e0ce9
73 changed files with 8537 additions and 2 deletions

102
tests/build.go Normal file
View File

@@ -0,0 +1,102 @@
package tests
import (
"errors"
"fmt"
"os"
"testing"
"github.com/containerd/containerd/platforms"
"github.com/containerd/continuity/fs/fstest"
"github.com/moby/buildkit/util/contentutil"
"github.com/moby/buildkit/util/testutil"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/stretchr/testify/require"
)
func buildCmd(sb integration.Sandbox, args ...string) (string, error) {
args = append([]string{"build", "--progress=quiet"}, args...)
cmd := buildxCmd(sb, args...)
out, err := cmd.CombinedOutput()
return string(out), err
}
var buildTests = []func(t *testing.T, sb integration.Sandbox){
testBuild,
testBuildLocalExport,
testBuildRegistryExport,
testBuildTarExport,
}
func testBuild(t *testing.T, sb integration.Sandbox) {
dir := createTestProject(t)
out, err := buildCmd(sb, dir)
require.NoError(t, err, string(out))
}
func testBuildLocalExport(t *testing.T, sb integration.Sandbox) {
dir := createTestProject(t)
out, err := buildCmd(sb, fmt.Sprintf("--output=type=local,dest=%s/result", dir), dir)
require.NoError(t, err, string(out))
dt, err := os.ReadFile(dir + "/result/bar")
require.NoError(t, err)
require.Equal(t, "foo", string(dt))
}
func testBuildTarExport(t *testing.T, sb integration.Sandbox) {
dir := createTestProject(t)
out, err := buildCmd(sb, fmt.Sprintf("--output=type=tar,dest=%s/result.tar", dir), dir)
require.NoError(t, err, string(out))
dt, err := os.ReadFile(fmt.Sprintf("%s/result.tar", dir))
require.NoError(t, err)
m, err := testutil.ReadTarToMap(dt, false)
require.NoError(t, err)
require.Contains(t, m, "bar")
require.Equal(t, "foo", string(m["bar"].Data))
}
func testBuildRegistryExport(t *testing.T, sb integration.Sandbox) {
dir := createTestProject(t)
registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
target := registry + "/buildx/registry:latest"
out, err := buildCmd(sb, fmt.Sprintf("--output=type=image,name=%s,push=true", target), dir)
require.NoError(t, err, string(out))
desc, provider, err := contentutil.ProviderFromRef(target)
require.NoError(t, err)
imgs, err := testutil.ReadImages(sb.Context(), provider, desc)
require.NoError(t, err)
pk := platforms.Format(platforms.Normalize(platforms.DefaultSpec()))
img := imgs.Find(pk)
require.NotNil(t, img)
require.Len(t, img.Layers, 1)
require.Equal(t, img.Layers[0]["bar"].Data, []byte("foo"))
}
func createTestProject(t *testing.T) string {
dockerfile := []byte(`
FROM busybox:latest AS base
COPY foo /etc/foo
RUN cp /etc/foo /etc/bar
FROM scratch
COPY --from=base /etc/bar /bar
`)
dir, err := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
require.NoError(t, err)
return dir
}

38
tests/inspect.go Normal file
View File

@@ -0,0 +1,38 @@
package tests
import (
"strings"
"testing"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/stretchr/testify/require"
)
func inspectCmd(sb integration.Sandbox, args ...string) (string, error) {
args = append([]string{"inspect"}, args...)
cmd := buildxCmd(sb, args...)
out, err := cmd.CombinedOutput()
return string(out), err
}
var inspectTests = []func(t *testing.T, sb integration.Sandbox){
testInspect,
}
func testInspect(t *testing.T, sb integration.Sandbox) {
out, err := inspectCmd(sb)
require.NoError(t, err, string(out))
var name string
var driver string
for _, line := range strings.Split(out, "\n") {
if v, ok := strings.CutPrefix(line, "Name:"); ok && name == "" {
name = strings.TrimSpace(v)
}
if v, ok := strings.CutPrefix(line, "Driver:"); ok && driver == "" {
driver = strings.TrimSpace(v)
}
}
require.Equal(t, sb.Address(), name)
require.Equal(t, sb.Name(), driver)
}

30
tests/integration.go Normal file
View File

@@ -0,0 +1,30 @@
package tests
import (
"os"
"os/exec"
"testing"
"github.com/containerd/continuity/fs/fstest"
"github.com/moby/buildkit/util/testutil/integration"
)
func tmpdir(t *testing.T, appliers ...fstest.Applier) (string, error) {
tmpdir := t.TempDir()
if err := fstest.Apply(appliers...).Apply(tmpdir); err != nil {
return "", err
}
return tmpdir, nil
}
func buildxCmd(sb integration.Sandbox, args ...string) *exec.Cmd {
if builder := sb.Address(); builder != "" {
args = append([]string{"--builder=" + builder}, args...)
}
cmd := exec.Command("buildx", args...)
if context := sb.DockerAddress(); context != "" {
cmd.Env = append(os.Environ(), "DOCKER_CONTEXT="+context)
}
return cmd
}

45
tests/integration_test.go Normal file
View File

@@ -0,0 +1,45 @@
package tests
import (
"os"
"testing"
"github.com/distribution/distribution/v3/reference"
"github.com/docker/buildx/tests/workers"
"github.com/moby/buildkit/util/testutil/integration"
)
func init() {
if integration.IsTestDockerd() {
workers.InitDockerWorker()
workers.InitDockerContainerWorker()
} else {
workers.InitRemoteWorker()
}
}
func TestIntegration(t *testing.T) {
var tests []func(t *testing.T, sb integration.Sandbox)
tests = append(tests, buildTests...)
tests = append(tests, inspectTests...)
tests = append(tests, lsTests...)
testIntegration(t, tests...)
}
func testIntegration(t *testing.T, funcs ...func(t *testing.T, sb integration.Sandbox)) {
mirroredImages := integration.OfficialImages("busybox:latest", "alpine:latest")
buildkitImage := "docker.io/moby/buildkit:buildx-stable-1"
if integration.IsTestDockerd() {
if img, ok := os.LookupEnv("TEST_BUILDKIT_IMAGE"); ok {
ref, err := reference.ParseNormalizedNamed(img)
if err == nil {
buildkitImage = ref.String()
}
}
}
mirroredImages["moby/buildkit:buildx-stable-1"] = buildkitImage
mirrors := integration.WithMirroredImages(mirroredImages)
tests := integration.TestFuncs(funcs...)
integration.Run(t, tests, mirrors)
}

33
tests/ls.go Normal file
View File

@@ -0,0 +1,33 @@
package tests
import (
"strings"
"testing"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/stretchr/testify/require"
)
func lsCmd(sb integration.Sandbox, args ...string) (string, error) {
args = append([]string{"ls"}, args...)
cmd := buildxCmd(sb, args...)
out, err := cmd.CombinedOutput()
return string(out), err
}
var lsTests = []func(t *testing.T, sb integration.Sandbox){
testLs,
}
func testLs(t *testing.T, sb integration.Sandbox) {
out, err := lsCmd(sb)
require.NoError(t, err, string(out))
for _, line := range strings.Split(out, "\n") {
if strings.Contains(line, sb.Address()) {
require.Contains(t, line, sb.Name())
return
}
}
require.Fail(t, out)
}

26
tests/workers/backend.go Normal file
View File

@@ -0,0 +1,26 @@
package workers
type backend struct {
builder string
context string
}
func (s *backend) Address() string {
return s.builder
}
func (s *backend) DockerAddress() string {
return s.context
}
func (s *backend) ContainerdAddress() string {
return ""
}
func (s *backend) Snapshotter() string {
return ""
}
func (s *backend) Rootless() bool {
return false
}

View File

@@ -0,0 +1,66 @@
package workers
import (
"context"
"os"
"os/exec"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors"
)
func InitDockerContainerWorker() {
integration.Register(&containerWorker{
id: "docker-container",
})
}
type containerWorker struct {
id string
}
func (w *containerWorker) Name() string {
return w.id
}
func (w *containerWorker) Rootless() bool {
return false
}
func (w *containerWorker) New(ctx context.Context, cfg *integration.BackendConfig) (integration.Backend, func() error, error) {
bk, bkclose, err := dockerWorker{id: w.id}.New(ctx, cfg)
if err != nil {
return bk, bkclose, err
}
name := "integration-container-" + identity.NewID()
cmd := exec.Command("buildx", "create",
"--bootstrap",
"--name="+name,
"--config="+cfg.ConfigFile,
"--driver=docker-container",
"--driver-opt=network=host",
)
cmd.Env = append(os.Environ(), "DOCKER_CONTEXT="+bk.DockerAddress())
if err := cmd.Run(); err != nil {
return nil, nil, errors.Wrapf(err, "failed to create buildx instance %s", name)
}
cl := func() error {
var err error
if err1 := bkclose(); err == nil {
err = err1
}
cmd := exec.Command("buildx", "rm", "-f", name)
if err1 := cmd.Run(); err == nil {
err = err1
}
return err
}
return &backend{
context: bk.DockerAddress(),
builder: name,
}, cl, nil
}

64
tests/workers/docker.go Normal file
View File

@@ -0,0 +1,64 @@
package workers
import (
"context"
"os/exec"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors"
)
func InitDockerWorker() {
integration.Register(&dockerWorker{
id: "docker",
})
}
type dockerWorker struct {
id string
}
func (c dockerWorker) Name() string {
return c.id
}
func (c dockerWorker) Rootless() bool {
return false
}
func (c dockerWorker) New(ctx context.Context, cfg *integration.BackendConfig) (b integration.Backend, cl func() error, err error) {
moby := integration.Moby{
ID: c.id,
}
bk, bkclose, err := moby.New(ctx, cfg)
if err != nil {
return bk, cl, err
}
name := "integration-" + identity.NewID()
cmd := exec.Command("docker", "context", "create",
name,
"--docker", "host="+bk.DockerAddress(),
)
if err := cmd.Run(); err != nil {
return nil, cl, errors.Wrapf(err, "failed to create buildx instance %s", name)
}
cl = func() error {
var err error
if err1 := bkclose(); err == nil {
err = err1
}
cmd := exec.Command("docker", "context", "rm", "-f", name)
if err1 := cmd.Run(); err1 != nil {
err = errors.Wrapf(err1, "failed to remove buildx instance %s", name)
}
return err
}
return &backend{
builder: name,
context: name,
}, cl, nil
}

63
tests/workers/remote.go Normal file
View File

@@ -0,0 +1,63 @@
package workers
import (
"context"
"os/exec"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors"
)
func InitRemoteWorker() {
integration.Register(&remoteWorker{
id: "remote",
})
}
type remoteWorker struct {
id string
}
func (w remoteWorker) Name() string {
return w.id
}
func (w remoteWorker) Rootless() bool {
return false
}
func (w remoteWorker) New(ctx context.Context, cfg *integration.BackendConfig) (b integration.Backend, cl func() error, err error) {
oci := integration.OCI{ID: w.id}
bk, bkclose, err := oci.New(ctx, cfg)
if err != nil {
return bk, cl, err
}
name := "integration-remote-" + identity.NewID()
cmd := exec.Command("buildx", "create",
"--bootstrap",
"--name="+name,
"--driver=remote",
bk.Address(),
)
if err := cmd.Run(); err != nil {
return nil, nil, errors.Wrapf(err, "failed to create buildx instance %s", name)
}
cl = func() error {
var err error
if err1 := bkclose(); err == nil {
err = err1
}
cmd := exec.Command("buildx", "rm", "-f", name)
if err1 := cmd.Run(); err == nil {
err = err1
}
return err
}
return &backend{
builder: name,
}, cl, nil
}