Merge pull request #687 from tonistiigi/dockerfile-testing

dockerfile: add testing external dockerfile features
docker-18.09
Akihiro Suda 2018-10-16 15:58:55 +09:00 committed by GitHub
commit 0d80bd17a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 250 additions and 74 deletions

View File

@ -1,10 +1,15 @@
FROM --platform=$BUILDPLATFORM golang:1.11-alpine AS builder # syntax = tonistiigi/dockerfile:runmount20181002
FROM --platform=$BUILDPLATFORM golang:1.11 AS builder
RUN apt-get update && apt-get install -y --no-install-recommends file
ARG BUILDTAGS="" ARG BUILDTAGS=""
COPY . /go/src/github.com/moby/buildkit
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
ENV GOOS=$TARGETOS GOARCH=$TARGETARCH ENV GOOS=$TARGETOS GOARCH=$TARGETARCH
RUN CGO_ENABLED=0 go build -o /dockerfile-frontend -tags "$BUILDTAGS" --ldflags '-extldflags "-static"' github.com/moby/buildkit/frontend/dockerfile/cmd/dockerfile-frontend WORKDIR /go/src/github.com/moby/buildkit
RUN --mount=target=. --mount=type=cache,target=/root/.cache \
CGO_ENABLED=0 go build -o /dockerfile-frontend -tags "$BUILDTAGS" --ldflags '-extldflags "-static"' ./frontend/dockerfile/cmd/dockerfile-frontend && \
file /dockerfile-frontend | grep "statically linked"
FROM scratch FROM scratch
COPY --from=builder /dockerfile-frontend /bin/dockerfile-frontend COPY --from=builder /dockerfile-frontend /bin/dockerfile-frontend

View File

@ -1,4 +1,4 @@
// +build !dfrunmount,!dfextall // +build !dfrunmount
package dockerfile2llb package dockerfile2llb

View File

@ -1,4 +1,4 @@
// +build dfrunmount dfextall // +build dfrunmount
package dockerfile2llb package dockerfile2llb

View File

@ -1,4 +1,4 @@
// +build dfsecrets dfextall // +build dfsecrets
package dockerfile2llb package dockerfile2llb

View File

@ -1,4 +1,4 @@
// +build dfssh dfextall // +build dfssh
package dockerfile2llb package dockerfile2llb

View File

@ -0,0 +1,51 @@
// +build dfrunmount
package dockerfile
import (
"context"
"os"
"testing"
"github.com/containerd/continuity/fs/fstest"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/frontend/dockerfile/builder"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/stretchr/testify/require"
)
var mountTests = []integration.Test{
testMountContext,
}
func init() {
allTests = append(allTests, mountTests...)
}
func testMountContext(t *testing.T, sb integration.Sandbox) {
f := getFrontend(t, sb)
dockerfile := []byte(`
FROM busybox
RUN --mount=target=/context [ "$(cat /context/testfile)" == "contents0" ]
`)
dir, err := tmpdir(
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("testfile", []byte("contents0"), 0600),
)
require.NoError(t, err)
defer os.RemoveAll(dir)
c, err := client.New(context.TODO(), sb.Address())
require.NoError(t, err)
defer c.Close()
_, err = f.Solve(context.TODO(), c, client.SolveOpt{
LocalDirs: map[string]string{
builder.LocalNameDockerfile: dir,
builder.LocalNameContext: dir,
},
}, nil)
require.NoError(t, err)
}

View File

@ -37,43 +37,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var opts []integration.TestOpt var allTests = []integration.Test{
type frontend interface {
Solve(context.Context, *client.Client, client.SolveOpt, chan *client.SolveStatus) (*client.SolveResponse, error)
DFCmdArgs(string, string) (string, string)
RequiresBuildctl(t *testing.T)
}
func init() {
frontends := map[string]interface{}{}
opts = []integration.TestOpt{
integration.WithMirroredImages(integration.OfficialImages("busybox:latest")),
integration.WithMirroredImages(map[string]string{
"tonistiigi/copy:v0.1.7": "docker.io/" + dockerfile2llb.DefaultCopyImage,
}),
integration.WithMatrix("frontend", frontends),
}
if os.Getenv("FRONTEND_BUILTIN_ONLY") == "1" {
frontends["builtin"] = &builtinFrontend{}
} else if os.Getenv("FRONTEND_CLIENT_ONLY") == "1" {
frontends["client"] = &clientFrontend{}
} else if gw := os.Getenv("FRONTEND_GATEWAY_ONLY"); gw != "" {
name := "buildkit_test/" + identity.NewID() + ":latest"
opts = append(opts, integration.WithMirroredImages(map[string]string{
name: gw,
}))
frontends["gateway"] = &gatewayFrontend{gw: name}
} else {
frontends["builtin"] = &builtinFrontend{}
frontends["client"] = &clientFrontend{}
}
}
func TestIntegration(t *testing.T) {
integration.Run(t, []integration.Test{
testNoSnapshotLeak, testNoSnapshotLeak,
testCmdShell, testCmdShell,
testGlobalArg, testGlobalArg,
@ -116,7 +80,45 @@ func TestIntegration(t *testing.T) {
testCopyThroughSymlinkMultiStage, testCopyThroughSymlinkMultiStage,
testCopyChownCreateDest, testCopyChownCreateDest,
testEmptyDestDir, testEmptyDestDir,
}, opts...) }
var opts []integration.TestOpt
type frontend interface {
Solve(context.Context, *client.Client, client.SolveOpt, chan *client.SolveStatus) (*client.SolveResponse, error)
DFCmdArgs(string, string) (string, string)
RequiresBuildctl(t *testing.T)
}
func init() {
frontends := map[string]interface{}{}
opts = []integration.TestOpt{
integration.WithMirroredImages(integration.OfficialImages("busybox:latest")),
integration.WithMirroredImages(map[string]string{
"tonistiigi/copy:v0.1.7": "docker.io/" + dockerfile2llb.DefaultCopyImage,
}),
integration.WithMatrix("frontend", frontends),
}
if os.Getenv("FRONTEND_BUILTIN_ONLY") == "1" {
frontends["builtin"] = &builtinFrontend{}
} else if os.Getenv("FRONTEND_CLIENT_ONLY") == "1" {
frontends["client"] = &clientFrontend{}
} else if gw := os.Getenv("FRONTEND_GATEWAY_ONLY"); gw != "" {
name := "buildkit_test/" + identity.NewID() + ":latest"
opts = append(opts, integration.WithMirroredImages(map[string]string{
name: gw,
}))
frontends["gateway"] = &gatewayFrontend{gw: name}
} else {
frontends["builtin"] = &builtinFrontend{}
frontends["client"] = &clientFrontend{}
}
}
func TestIntegration(t *testing.T) {
integration.Run(t, allTests, opts...)
} }
func testEmptyDestDir(t *testing.T, sb integration.Sandbox) { func testEmptyDestDir(t *testing.T, sb integration.Sandbox) {

View File

@ -1,4 +1,4 @@
// +build !dfsecrets,!dfextall // +build !dfsecrets
package instructions package instructions

View File

@ -1,4 +1,4 @@
// +build !dfssh,!dfextall // +build !dfssh
package instructions package instructions

View File

@ -1,4 +1,4 @@
// +build dfrunmount dfextall // +build dfrunmount
package instructions package instructions

View File

@ -1,4 +1,4 @@
// +build dfsecrets dfextall // +build dfsecrets
package instructions package instructions

View File

@ -1,4 +1,4 @@
// +build dfssh dfextall // +build dfssh
package instructions package instructions

View File

@ -0,0 +1 @@
dfrunmount dfsecrets dfssh

View File

@ -0,0 +1 @@
dfrunmount

View File

@ -0,0 +1 @@
dfrunmount dfsecrets

View File

@ -0,0 +1 @@
dfrunmount dfssh

View File

@ -4,7 +4,7 @@ ARG CONTAINERD_VERSION=v1.2.0-rc.1
ARG CONTAINERD10_VERSION=v1.0.3 ARG CONTAINERD10_VERSION=v1.0.3
# available targets: buildkitd, buildkitd.oci_only, buildkitd.containerd_only # available targets: buildkitd, buildkitd.oci_only, buildkitd.containerd_only
ARG BUILDKIT_TARGET=buildkitd ARG BUILDKIT_TARGET=buildkitd
ARG REGISTRY_VERSION=2.6 ARG REGISTRY_VERSION=v2.7.0-rc.0
ARG ROOTLESSKIT_VERSION=4f7ae4607d626f0a22fb495056d55b17cce8c01b ARG ROOTLESSKIT_VERSION=4f7ae4607d626f0a22fb495056d55b17cce8c01b
# The `buildkitd` stage and the `buildctl` stage are placed here # The `buildkitd` stage and the `buildctl` stage are placed here
@ -74,7 +74,7 @@ FROM buildkit-base AS buildkitd.containerd_only
ENV CGO_ENABLED=0 ENV CGO_ENABLED=0
RUN go build -ldflags "$(cat .tmp/ldflags) -d" -o /usr/bin/buildkitd.containerd_only -tags no_oci_worker ./cmd/buildkitd RUN go build -ldflags "$(cat .tmp/ldflags) -d" -o /usr/bin/buildkitd.containerd_only -tags no_oci_worker ./cmd/buildkitd
FROM registry:$REGISTRY_VERSION AS registry FROM tonistiigi/registry:$REGISTRY_VERSION AS registry
FROM gobuild-base AS rootlesskit-base FROM gobuild-base AS rootlesskit-base
RUN git clone https://github.com/rootless-containers/rootlesskit.git /go/src/github.com/rootless-containers/rootlesskit RUN git clone https://github.com/rootless-containers/rootlesskit.git /go/src/github.com/rootless-containers/rootlesskit

View File

@ -6,7 +6,7 @@ ARG CONTAINERD_VERSION=v1.2.0-rc.1
ARG CONTAINERD10_VERSION=v1.0.3 ARG CONTAINERD10_VERSION=v1.0.3
# available targets: buildkitd, buildkitd.oci_only, buildkitd.containerd_only # available targets: buildkitd, buildkitd.oci_only, buildkitd.containerd_only
ARG BUILDKIT_TARGET=buildkitd ARG BUILDKIT_TARGET=buildkitd
ARG REGISTRY_VERSION=2.6 ARG REGISTRY_VERSION=v2.7.0-rc.0
ARG ROOTLESSKIT_VERSION=4f7ae4607d626f0a22fb495056d55b17cce8c01b ARG ROOTLESSKIT_VERSION=4f7ae4607d626f0a22fb495056d55b17cce8c01b
ARG ROOTLESS_BASE_MODE=external ARG ROOTLESS_BASE_MODE=external
@ -149,7 +149,7 @@ RUN --mount=from=containerd-src,src=/usr/src/containerd,readwrite --mount=target
&& make bin/containerd-shim \ && make bin/containerd-shim \
&& mv bin /out && mv bin /out
FROM registry:$REGISTRY_VERSION AS registry FROM tonistiigi/registry:$REGISTRY_VERSION AS registry
FROM gobuild-base AS rootlesskit FROM gobuild-base AS rootlesskit
ARG ROOTLESSKIT_VERSION ARG ROOTLESSKIT_VERSION
@ -188,7 +188,7 @@ ENTRYPOINT ["containerd"]
FROM buildkit-base AS integration-tests FROM buildkit-base AS integration-tests
ENV BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR="1000:1000" ENV BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR="1000:1000"
RUN apt-get install -y --no-install-recommends uidmap sudo musl \ RUN apt-get install -y --no-install-recommends uidmap sudo \
&& useradd --create-home --home-dir /home/user --uid 1000 -s /bin/sh user \ && useradd --create-home --home-dir /home/user --uid 1000 -s /bin/sh user \
&& echo "XDG_RUNTIME_DIR=/run/user/1000; export XDG_RUNTIME_DIR" >> /home/user/.profile \ && echo "XDG_RUNTIME_DIR=/run/user/1000; export XDG_RUNTIME_DIR" >> /home/user/.profile \
&& mkdir -m 0700 -p /run/user/1000 \ && mkdir -m 0700 -p /run/user/1000 \

View File

@ -6,7 +6,9 @@ set -eu -o pipefail
: ${TEST_INTEGRATION=} : ${TEST_INTEGRATION=}
: ${TEST_GATEWAY=} : ${TEST_GATEWAY=}
: ${TEST_DOCKERFILE=} : ${TEST_DOCKERFILE=}
: ${DOCKERFILE_RELEASES=}
: ${CONTINUOUS_INTEGRATION=} : ${CONTINUOUS_INTEGRATION=}
: ${BUILDKIT_REGISTRY_MIRROR_DIR=}
progressFlag="" progressFlag=""
if [ "$CONTINUOUS_INTEGRATION" == "true" ]; then progressFlag="--progress=plain"; fi if [ "$CONTINUOUS_INTEGRATION" == "true" ]; then progressFlag="--progress=plain"; fi
@ -32,7 +34,6 @@ while test $# -gt 0
shift shift
done done
iid="buildkit-tests" iid="buildkit-tests"
iidfile=$(mktemp -t docker-iidfile.XXXXXXXXXX) iidfile=$(mktemp -t docker-iidfile.XXXXXXXXXX)
set -x set -x
@ -61,8 +62,10 @@ case $buildmode in
;; ;;
esac esac
cacheVolume=$(docker create -v /root/.cache -v /root/.cache/registry alpine)
if [ "$TEST_INTEGRATION" == 1 ]; then if [ "$TEST_INTEGRATION" == 1 ]; then
docker run --rm -v /tmp --privileged $iid go test ${TESTFLAGS:--v} ${TESTPKGS:-./...} docker run --rm -v /tmp --volumes-from=$cacheVolume -e BUILDKIT_REGISTRY_MIRROR_DIR=/root/.cache/registry --privileged $iid go test ${TESTFLAGS:--v} ${TESTPKGS:-./...}
fi fi
@ -72,13 +75,42 @@ fi
if [ "$TEST_DOCKERFILE" == 1 ]; then if [ "$TEST_DOCKERFILE" == 1 ]; then
docker run --rm $iid go build ./frontend/dockerfile/cmd/dockerfile-frontend if [ -z $DOCKERFILE_RELEASES ]; then
docker run --rm $iid go build -tags dfrunmount ./frontend/dockerfile/cmd/dockerfile-frontend DOCKERFILE_RELEASES="mainline experimental mounts secrets ssh"
docker run --rm $iid go build -tags "dfrunmount dfsecrets" ./frontend/dockerfile/cmd/dockerfile-frontend fi
docker run --rm $iid go build -tags "dfrunmount dfssh" ./frontend/dockerfile/cmd/dockerfile-frontend
docker run --rm $iid go build -tags dfextall ./frontend/dockerfile/cmd/dockerfile-frontend
for release in $DOCKERFILE_RELEASES; do
buildtags=$(cat ./frontend/dockerfile/release/$release/tags)
tarout=$(mktemp -t dockerfile-frontend.XXXXXXXXXX)
case $buildmode in
"buildkit")
buildctl build $progressFlag --frontend=dockerfile.v0 --local context=. --local dockerfile=. \
--frontend-opt filename=./frontend/dockerfile/cmd/dockerfile-frontend/Dockerfile \
--frontend-opt build-arg:BUILDTAGS="$buildtags" \
--exporter=oci --exporter-opt output=$tarout
;;
"docker-buildkit")
dfiidfile=$(mktemp -t docker-iidfile.XXXXXXXXXX)
docker build --iidfile=$dfiidfile -f ./frontend/dockerfile/cmd/dockerfile-frontend/Dockerfile --build-arg BUILDTAGS="$buildtags" .
dfiid=$(cat $dfiidfile)
docker save -o $tarout $dfiid
docker rmi $dfiid
rm $dfiidfile
;;
esac
if [ -s $tarout ]; then
cid=$(docker create -v /tmp --rm --privileged --volumes-from=$cacheVolume -e BUILDKIT_REGISTRY_MIRROR_DIR=/root/.cache/registry -e BUILDKIT_WORKER_RANDOM=1 -e FRONTEND_GATEWAY_ONLY=local:/$release.tar -e EXTERNAL_DF_FRONTEND=/dockerfile-frontend $iid go test --count=1 -tags "$buildtags" ${TESTFLAGS:--v} ./frontend/dockerfile)
docker cp $tarout $cid:/$release.tar
docker start -a $cid
fi
rm $tarout
done
fi fi
docker rm -v $cacheVolume
case $buildmode in case $buildmode in
"docker-buildkit") "docker-buildkit")
rm "$iidfile" rm "$iidfile"

View File

@ -72,6 +72,7 @@ func (w *ingester) Writer(ctx context.Context, opts ...content.WriterOpt) (conte
} }
writer, err := w.pusher.Push(ctx, wo.Desc) writer, err := w.pusher.Push(ctx, wo.Desc)
if err != nil { if err != nil {
unlock()
return nil, err return nil, err
} }
return &lockedWriter{unlock: unlock, Writer: writer}, nil return &lockedWriter{unlock: unlock, Writer: writer}, nil

View File

@ -0,0 +1,57 @@
package integration
import (
"context"
"encoding/json"
"io/ioutil"
"os"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/content/local"
"github.com/containerd/containerd/images/archive"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
func providerFromBinary(fn string) (_ ocispec.Descriptor, _ content.Provider, _ func(), err error) {
ctx := context.TODO()
tmpDir, err := ioutil.TempDir("", "buildkit-state")
if err != nil {
return ocispec.Descriptor{}, nil, nil, err
}
close := func() {
os.RemoveAll(tmpDir)
}
defer func() {
if err != nil {
close()
}
}()
// can't use contentutil.Buffer because ImportIndex takes content.Store even though only requires Provider/Ingester
c, err := local.NewStore(tmpDir)
if err != nil {
return ocispec.Descriptor{}, nil, nil, err
}
f, err := os.Open(fn)
if err != nil {
return ocispec.Descriptor{}, nil, nil, err
}
defer f.Close()
desc, err := archive.ImportIndex(ctx, c, f)
if err != nil {
return ocispec.Descriptor{}, nil, nil, err
}
var idx ocispec.Index
dt, err := content.ReadBlob(ctx, c, desc)
if err != nil {
return ocispec.Descriptor{}, nil, nil, err
}
if err := json.Unmarshal(dt, &idx); err != nil {
return ocispec.Descriptor{}, nil, nil, err
}
return idx.Manifests[0], c, close, nil
}

View File

@ -58,7 +58,7 @@ http:
} }
cmd := exec.Command("registry", "serve", filepath.Join(dir, "config.yaml")) cmd := exec.Command("registry", "serve", filepath.Join(dir, "config.yaml"))
rc, err := cmd.StdoutPipe() rc, err := cmd.StderrPipe()
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/rand"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -14,8 +15,11 @@ import (
"sync" "sync"
"syscall" "syscall"
"testing" "testing"
"time"
"github.com/containerd/containerd/content"
"github.com/moby/buildkit/util/contentutil" "github.com/moby/buildkit/util/contentutil"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -123,7 +127,13 @@ func Run(t *testing.T, testCases []Test, opt ...TestOpt) {
matrix := prepareValueMatrix(tc) matrix := prepareValueMatrix(tc)
for _, br := range List() { list := List()
if os.Getenv("BUILDKIT_WORKER_RANDOM") == "1" && len(list) > 0 {
rand.Seed(time.Now().UnixNano())
list = []Worker{list[rand.Intn(len(list))]}
}
for _, br := range list {
for _, tc := range testCases { for _, tc := range testCases {
for _, mv := range matrix { for _, mv := range matrix {
fn := getFunctionName(tc) fn := getFunctionName(tc)
@ -177,10 +187,24 @@ func copyImagesLocal(t *testing.T, host string, images map[string]string) error
} }
localImageCache[host][to] = struct{}{} localImageCache[host][to] = struct{}{}
desc, provider, err := contentutil.ProviderFromRef(from) var desc ocispec.Descriptor
var provider content.Provider
var err error
if strings.HasPrefix(from, "local:") {
var closer func()
desc, provider, closer, err = providerFromBinary(strings.TrimPrefix(from, "local:"))
if err != nil { if err != nil {
return err return err
} }
if closer != nil {
defer closer()
}
} else {
desc, provider, err = contentutil.ProviderFromRef(from)
if err != nil {
return err
}
}
ingester, err := contentutil.IngesterFromRef(host + "/" + to) ingester, err := contentutil.IngesterFromRef(host + "/" + to)
if err != nil { if err != nil {
return err return err