368 lines
10 KiB
Go
368 lines
10 KiB
Go
package tests
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/containerd/continuity/fs/fstest"
|
|
"github.com/docker/buildx/util/gitutil"
|
|
"github.com/docker/buildx/util/gitutil/gittestutil"
|
|
bkgitutil "github.com/moby/buildkit/util/gitutil"
|
|
"github.com/moby/buildkit/util/testutil/integration"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var historyTests = []func(t *testing.T, sb integration.Sandbox){
|
|
testHistoryExport,
|
|
testHistoryExportFinalize,
|
|
testHistoryExportFinalizeMultiNodeRef,
|
|
testHistoryExportFinalizeMultiNodeAll,
|
|
testHistoryInspect,
|
|
testHistoryLs,
|
|
testHistoryRm,
|
|
testHistoryLsStoppedBuilder,
|
|
testHistoryBuildName,
|
|
}
|
|
|
|
func testHistoryExport(t *testing.T, sb integration.Sandbox) {
|
|
ref := buildTestProject(t, sb)
|
|
require.NotEmpty(t, ref.Ref)
|
|
requireHistoryRef(t, sb, ref.Ref)
|
|
|
|
outFile := path.Join(t.TempDir(), "export.dockerbuild")
|
|
cmd := buildxCmd(sb, withArgs("history", "export", ref.Ref, "--output", outFile))
|
|
out, err := cmd.CombinedOutput()
|
|
require.NoError(t, err, string(out))
|
|
require.FileExists(t, outFile)
|
|
}
|
|
|
|
func testHistoryExportFinalize(t *testing.T, sb integration.Sandbox) {
|
|
ref := buildTestProject(t, sb)
|
|
require.NotEmpty(t, ref.Ref)
|
|
requireHistoryRef(t, sb, ref.Ref)
|
|
|
|
outFile := path.Join(t.TempDir(), "export.dockerbuild")
|
|
cmd := buildxCmd(sb, withArgs("history", "export", ref.Ref, "--finalize", "--output", outFile))
|
|
out, err := cmd.CombinedOutput()
|
|
require.NoError(t, err, string(out))
|
|
require.FileExists(t, outFile)
|
|
}
|
|
|
|
func testHistoryExportFinalizeMultiNodeRef(t *testing.T, sb integration.Sandbox) {
|
|
if !isRemoteMultiNodeWorker(sb) {
|
|
t.Skip("only testing with remote multi-node worker")
|
|
}
|
|
|
|
ref := buildTestProject(t, sb, withArgs("--platform=linux/amd64,linux/arm64", "--output=type=cacheonly"))
|
|
require.NotEmpty(t, ref.Ref)
|
|
requireHistoryRef(t, sb, ref.Ref)
|
|
|
|
outFile := path.Join(t.TempDir(), "export.dockerbuild")
|
|
cmd := buildxCmd(sb, withArgs("history", "export", ref.Ref, "--finalize", "--output", outFile))
|
|
out, err := cmd.CombinedOutput()
|
|
require.NoError(t, err, string(out))
|
|
require.FileExists(t, outFile)
|
|
}
|
|
|
|
func testHistoryExportFinalizeMultiNodeAll(t *testing.T, sb integration.Sandbox) {
|
|
if !isRemoteMultiNodeWorker(sb) {
|
|
t.Skip("only testing with remote multi-node worker")
|
|
}
|
|
|
|
ref := buildTestProject(t, sb, withArgs("--platform=linux/amd64,linux/arm64", "--output=type=cacheonly"))
|
|
require.NotEmpty(t, ref.Ref)
|
|
requireHistoryRef(t, sb, ref.Ref)
|
|
|
|
outFile := path.Join(t.TempDir(), "export.dockerbuild")
|
|
cmd := buildxCmd(sb, withArgs("history", "export", "--finalize", "--all", "--output", outFile))
|
|
out, err := cmd.CombinedOutput()
|
|
require.NoError(t, err, string(out))
|
|
require.FileExists(t, outFile)
|
|
}
|
|
|
|
func testHistoryInspect(t *testing.T, sb integration.Sandbox) {
|
|
ref := buildTestProject(t, sb)
|
|
require.NotEmpty(t, ref.Ref)
|
|
|
|
cmd := buildxCmd(sb, withArgs("history", "inspect", ref.Ref, "--format=json"))
|
|
out, err := cmd.CombinedOutput()
|
|
require.NoError(t, err, string(out))
|
|
|
|
type recT struct {
|
|
Name string
|
|
Ref string
|
|
Context string
|
|
Dockerfile string
|
|
StartedAt *time.Time
|
|
CompletedAt *time.Time
|
|
Duration time.Duration
|
|
Status string
|
|
NumCompletedSteps int32
|
|
NumTotalSteps int32
|
|
NumCachedSteps int32
|
|
}
|
|
var rec recT
|
|
err = json.Unmarshal(out, &rec)
|
|
require.NoError(t, err)
|
|
require.Equal(t, ref.Ref, rec.Ref)
|
|
require.NotEmpty(t, rec.Name)
|
|
}
|
|
|
|
func testHistoryLs(t *testing.T, sb integration.Sandbox) {
|
|
ref := buildTestProject(t, sb)
|
|
require.NotEmpty(t, ref.Ref)
|
|
|
|
rec := requireHistoryRecord(t, sb, ref.String())
|
|
require.Equal(t, ref.String(), rec.Ref)
|
|
require.NotEmpty(t, rec.Name)
|
|
}
|
|
|
|
func testHistoryRm(t *testing.T, sb integration.Sandbox) {
|
|
ref := buildTestProject(t, sb)
|
|
require.NotEmpty(t, ref.Ref)
|
|
|
|
cmd := buildxCmd(sb, withArgs("history", "rm", ref.Ref))
|
|
out, err := cmd.Output()
|
|
require.NoError(t, err, string(out))
|
|
}
|
|
|
|
func testHistoryLsStoppedBuilder(t *testing.T, sb integration.Sandbox) {
|
|
if !isDockerContainerWorker(sb) {
|
|
t.Skip("only testing with docker-container worker")
|
|
}
|
|
|
|
var builderName string
|
|
t.Cleanup(func() {
|
|
if builderName == "" {
|
|
return
|
|
}
|
|
out, err := rmCmd(sb, withArgs(builderName))
|
|
require.NoError(t, err, out)
|
|
})
|
|
|
|
out, err := createCmd(sb, withArgs("--driver", "docker-container"))
|
|
require.NoError(t, err, out)
|
|
builderName = strings.TrimSpace(out)
|
|
|
|
ref := buildTestProject(t, sb)
|
|
require.NotEmpty(t, ref.Ref)
|
|
|
|
cmd := buildxCmd(sb, withArgs("stop", builderName))
|
|
bout, err := cmd.CombinedOutput()
|
|
require.NoError(t, err, string(bout))
|
|
|
|
cmd = buildxCmd(sb, withArgs("history", "ls", "--builder="+builderName, "--filter=ref="+ref.Ref, "--format=json"))
|
|
bout, err = cmd.CombinedOutput()
|
|
require.NoError(t, err, string(bout))
|
|
}
|
|
|
|
func testHistoryBuildName(t *testing.T, sb integration.Sandbox) {
|
|
t.Run("override", func(t *testing.T) {
|
|
dir := createTestProject(t)
|
|
out, err := buildCmd(sb, withArgs("--build-arg=BUILDKIT_BUILD_NAME=foobar", "--metadata-file", filepath.Join(dir, "md.json"), dir))
|
|
require.NoError(t, err, out)
|
|
|
|
dt, err := os.ReadFile(filepath.Join(dir, "md.json"))
|
|
require.NoError(t, err)
|
|
|
|
type mdT struct {
|
|
BuildRef string `json:"buildx.build.ref"`
|
|
}
|
|
var md mdT
|
|
err = json.Unmarshal(dt, &md)
|
|
require.NoError(t, err)
|
|
|
|
refParts := strings.Split(md.BuildRef, "/")
|
|
require.Len(t, refParts, 3)
|
|
|
|
rec := requireHistoryRecord(t, sb, md.BuildRef)
|
|
require.Equal(t, md.BuildRef, rec.Ref)
|
|
require.Equal(t, "foobar", rec.Name)
|
|
})
|
|
|
|
t.Run("git query", func(t *testing.T) {
|
|
dockerfile := []byte(`
|
|
FROM busybox:latest
|
|
COPY foo /foo
|
|
`)
|
|
dir := tmpdir(
|
|
t,
|
|
fstest.CreateFile("Dockerfile", dockerfile, 0600),
|
|
fstest.CreateFile("foo", []byte("foo"), 0600),
|
|
)
|
|
dirDest := t.TempDir()
|
|
|
|
git, err := gitutil.New(bkgitutil.WithDir(dir))
|
|
require.NoError(t, err)
|
|
|
|
gittestutil.GitInit(git, t)
|
|
gittestutil.GitAdd(git, t, "Dockerfile", "foo")
|
|
gittestutil.GitCommit(git, t, "initial commit")
|
|
addr := gittestutil.GitServeHTTP(git, t)
|
|
|
|
out, err := buildCmd(sb, withDir(dir),
|
|
withArgs("--output=type=local,dest="+dirDest, "--metadata-file", filepath.Join(dir, "md.json"), addr+"?ref=main"),
|
|
withEnv("BUILDX_SEND_GIT_QUERY_AS_INPUT=true"),
|
|
)
|
|
require.NoError(t, err, out)
|
|
require.FileExists(t, filepath.Join(dirDest, "foo"))
|
|
|
|
dt, err := os.ReadFile(filepath.Join(dir, "md.json"))
|
|
require.NoError(t, err)
|
|
|
|
type mdT struct {
|
|
BuildRef string `json:"buildx.build.ref"`
|
|
}
|
|
var md mdT
|
|
err = json.Unmarshal(dt, &md)
|
|
require.NoError(t, err)
|
|
|
|
refParts := strings.Split(md.BuildRef, "/")
|
|
require.Len(t, refParts, 3)
|
|
|
|
rec := requireHistoryRecord(t, sb, md.BuildRef)
|
|
require.Equal(t, md.BuildRef, rec.Ref)
|
|
require.Equal(t, addr+"#main", rec.Name)
|
|
})
|
|
|
|
t.Run("bake git", func(t *testing.T) {
|
|
bakefile := []byte(`
|
|
target "default" {
|
|
dockerfile-inline = <<EOT
|
|
FROM scratch
|
|
COPY foo /foo
|
|
EOT
|
|
}
|
|
`)
|
|
dir := tmpdir(
|
|
t,
|
|
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
|
|
fstest.CreateFile("foo", []byte("foo"), 0600),
|
|
)
|
|
dirDest := t.TempDir()
|
|
|
|
git, err := gitutil.New(bkgitutil.WithDir(dir))
|
|
require.NoError(t, err)
|
|
|
|
gittestutil.GitInit(git, t)
|
|
gittestutil.GitAdd(git, t, "docker-bake.hcl", "foo")
|
|
gittestutil.GitCommit(git, t, "initial commit")
|
|
addr := gittestutil.GitServeHTTP(git, t)
|
|
|
|
out, err := bakeCmd(sb, withDir(dir),
|
|
withArgs(addr, "--set", "*.output=type=local,dest="+dirDest, "--metadata-file", filepath.Join(dir, "md.json")),
|
|
)
|
|
require.NoError(t, err, out)
|
|
require.FileExists(t, filepath.Join(dirDest, "foo"))
|
|
|
|
dt, err := os.ReadFile(filepath.Join(dir, "md.json"))
|
|
require.NoError(t, err)
|
|
|
|
type mdT struct {
|
|
Default struct {
|
|
BuildRef string `json:"buildx.build.ref"`
|
|
} `json:"default"`
|
|
}
|
|
var md mdT
|
|
err = json.Unmarshal(dt, &md)
|
|
require.NoError(t, err)
|
|
|
|
refParts := strings.Split(md.Default.BuildRef, "/")
|
|
require.Len(t, refParts, 3)
|
|
|
|
rec := requireHistoryRecord(t, sb, md.Default.BuildRef)
|
|
require.Equal(t, md.Default.BuildRef, rec.Ref)
|
|
require.Equal(t, addr, rec.Name)
|
|
})
|
|
}
|
|
|
|
type buildRef struct {
|
|
Builder string
|
|
Node string
|
|
Ref string
|
|
}
|
|
|
|
func (b buildRef) String() string {
|
|
return b.Builder + "/" + b.Node + "/" + b.Ref
|
|
}
|
|
|
|
func buildTestProject(t *testing.T, sb integration.Sandbox, opts ...cmdOpt) buildRef {
|
|
dir := createTestProject(t)
|
|
opts = append(opts, withArgs("--metadata-file", filepath.Join(dir, "md.json"), dir))
|
|
out, err := buildCmd(sb, opts...)
|
|
require.NoError(t, err, out)
|
|
|
|
dt, err := os.ReadFile(filepath.Join(dir, "md.json"))
|
|
require.NoError(t, err)
|
|
|
|
type mdT struct {
|
|
BuildRef string `json:"buildx.build.ref"`
|
|
}
|
|
var md mdT
|
|
err = json.Unmarshal(dt, &md)
|
|
require.NoError(t, err)
|
|
|
|
refParts := strings.Split(md.BuildRef, "/")
|
|
require.Len(t, refParts, 3)
|
|
|
|
return buildRef{
|
|
Builder: refParts[0],
|
|
Node: refParts[1],
|
|
Ref: refParts[2],
|
|
}
|
|
}
|
|
|
|
func requireHistoryRef(t *testing.T, sb integration.Sandbox, ref string) {
|
|
cmd := buildxCmd(sb, withArgs("history", "ls", "--format={{.Ref}}"))
|
|
out, err := cmd.CombinedOutput()
|
|
require.NoError(t, err, string(out))
|
|
var matches int
|
|
for line := range strings.SplitSeq(strings.TrimSpace(string(out)), "\n") {
|
|
if strings.TrimSpace(line) == ref {
|
|
matches++
|
|
}
|
|
}
|
|
require.GreaterOrEqual(t, matches, 1)
|
|
}
|
|
|
|
type historyLsRecord struct {
|
|
Ref string `json:"ref"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"`
|
|
CreatedAt *time.Time `json:"created_at"`
|
|
CompletedAt *time.Time `json:"completed_at"`
|
|
TotalSteps int32 `json:"total_steps"`
|
|
CompletedSteps int32 `json:"completed_steps"`
|
|
CachedSteps int32 `json:"cached_steps"`
|
|
}
|
|
|
|
func requireHistoryRecord(t *testing.T, sb integration.Sandbox, ref string, opts ...cmdOpt) historyLsRecord {
|
|
cmd := buildxCmd(sb, append(opts, withArgs("history", "ls", "--format=json"))...)
|
|
out, err := cmd.CombinedOutput()
|
|
require.NoError(t, err, string(out))
|
|
|
|
for line := range strings.SplitSeq(strings.TrimSpace(string(out)), "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
continue
|
|
}
|
|
if !strings.HasPrefix(line, "{") {
|
|
continue
|
|
}
|
|
var rec historyLsRecord
|
|
err := json.Unmarshal([]byte(line), &rec)
|
|
require.NoError(t, err)
|
|
if rec.Ref == ref {
|
|
return rec
|
|
}
|
|
}
|
|
|
|
require.Failf(t, "history record not found", "ref %q was not found in history ls output:\n%s", ref, string(out))
|
|
return historyLsRecord{}
|
|
}
|