Merge pull request #188 from tonistiigi/empty-config

dockerfile: return valid empty image config for scratch
docker-18.09
Akihiro Suda 2017-12-05 16:47:11 +09:00 committed by GitHub
commit 81551b56a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 135 additions and 17 deletions

View File

@ -32,6 +32,9 @@ type blobmapper interface {
}
func GetDiffPairs(ctx context.Context, contentStore content.Store, snapshotter snapshot.Snapshotter, differ diff.Differ, ref cache.ImmutableRef) ([]DiffPair, error) {
if ref == nil {
return nil, nil
}
blobmap, ok := snapshotter.(blobmapper)
if !ok {
return nil, errors.Errorf("image exporter requires snapshotter with blobs mapping support")

View File

@ -52,6 +52,9 @@ func (s State) Marshal() (*Definition, error) {
def := &Definition{
Metadata: make(map[digest.Digest]OpMetadata, 0),
}
if s.Output() == nil {
return def, nil
}
def, err := marshal(s.Output().Vertex(), def, map[digest.Digest]struct{}{}, map[Vertex]struct{}{})
if err != nil {
return def, err

View File

@ -1,6 +1,8 @@
package local
import (
"io/ioutil"
"os"
"time"
"github.com/moby/buildkit/cache"
@ -56,21 +58,31 @@ func (e *localExporterInstance) Name() string {
}
func (e *localExporterInstance) Export(ctx context.Context, ref cache.ImmutableRef, opt map[string][]byte) error {
mount, err := ref.Mount(ctx, true)
if err != nil {
return err
}
var src string
var err error
if ref == nil {
src, err = ioutil.TempDir("", "buildkit")
if err != nil {
return err
}
defer os.RemoveAll(src)
} else {
mount, err := ref.Mount(ctx, true)
if err != nil {
return err
}
lm := snapshot.LocalMounter(mount)
lm := snapshot.LocalMounter(mount)
dest, err := lm.Mount()
if err != nil {
return err
src, err = lm.Mount()
if err != nil {
return err
}
defer lm.Unmount()
}
defer lm.Unmount()
progress := newProgressHandler(ctx, "copying files")
return filesync.CopyToCaller(ctx, dest, e.caller, progress)
return filesync.CopyToCaller(ctx, src, e.caller, progress)
}
func newProgressHandler(ctx context.Context, id string) func(int, bool) {

View File

@ -91,6 +91,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
if d.base == nil {
if d.stage.BaseName == emptyImageName {
d.state = llb.Scratch()
d.image = emptyImage()
continue
}
func(i int, d *dispatchState) {

View File

@ -1,9 +1,11 @@
package dockerfile2llb
import (
"runtime"
"time"
"github.com/docker/docker/api/types/strslice"
"github.com/moby/buildkit/util/system"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
@ -58,3 +60,16 @@ func clone(src Image) Image {
img.Config.Entrypoint = append([]string{}, src.Config.Entrypoint...)
return img
}
func emptyImage() Image {
img := Image{
Image: ocispec.Image{
Architecture: runtime.GOARCH,
OS: runtime.GOOS,
},
}
img.RootFS.Type = "layers"
img.Config.WorkingDir = "/"
img.Config.Env = []string{"PATH=" + system.DefaultPathEnv}
return img
}

View File

@ -4,18 +4,26 @@ import (
"archive/tar"
"bytes"
"compress/gzip"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/fs/fstest"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/platforms"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/util/testutil/httpserver"
"github.com/moby/buildkit/util/testutil/integration"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/require"
)
@ -25,6 +33,7 @@ func TestIntegration(t *testing.T) {
testDockerfileInvalidCommand,
testDockerfileADDFromURL,
testDockerfileAddArchive,
testDockerfileScratchConfig,
})
}
@ -342,6 +351,71 @@ ADD %s /
require.Equal(t, buf2.Bytes(), dt)
}
func testDockerfileScratchConfig(t *testing.T, sb integration.Sandbox) {
var cdAddress string
if cd, ok := sb.(interface {
ContainerdAddress() string
}); !ok {
t.Skip("only for containerd worker")
} else {
cdAddress = cd.ContainerdAddress()
}
t.Parallel()
dockerfile := []byte(`
FROM scratch
ENV foo=bar
`)
dir, err := tmpdir(
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)
require.NoError(t, err)
defer os.RemoveAll(dir)
args, trace := dfCmdArgs(dir, dir)
defer os.RemoveAll(trace)
target := "example.com/moby/dockerfilescratch:test"
cmd := sb.Cmd(args + " --exporter=image --exporter-opt=name=" + target)
err = cmd.Run()
require.NoError(t, err)
client, err := containerd.New(cdAddress)
require.NoError(t, err)
defer client.Close()
ctx := namespaces.WithNamespace(context.Background(), "buildkit")
img, err := client.ImageService().Get(ctx, target)
require.NoError(t, err)
desc, err := img.Config(ctx, client.ContentStore(), platforms.Default())
require.NoError(t, err)
dt, err := content.ReadBlob(ctx, client.ContentStore(), desc.Digest)
require.NoError(t, err)
var ociimg ocispec.Image
err = json.Unmarshal(dt, &ociimg)
require.NoError(t, err)
require.NotEqual(t, "", ociimg.OS)
require.NotEqual(t, "", ociimg.Architecture)
require.NotEqual(t, "", ociimg.Config.WorkingDir)
require.Equal(t, "layers", ociimg.RootFS.Type)
require.Contains(t, ociimg.Config.Env, "foo=bar")
require.Condition(t, func() bool {
for _, env := range ociimg.Config.Env {
if strings.HasPrefix(env, "PATH=") {
return true
}
}
return false
})
}
func tmpdir(appliers ...fstest.Applier) (string, error) {
tmpdir, err := ioutil.TempDir("", "buildkit-dockerfile")
if err != nil {

View File

@ -156,15 +156,21 @@ func (s *Solver) Solve(ctx context.Context, id string, req SolveRequest) error {
}
defer func() {
go ref.Release(context.TODO())
if ref != nil {
go ref.Release(context.TODO())
}
}()
immutable, ok := toImmutableRef(ref)
if !ok {
return errors.Errorf("invalid reference for exporting: %T", ref)
}
if err := immutable.Finalize(ctx); err != nil {
return err
var immutable cache.ImmutableRef
if ref != nil {
var ok bool
immutable, ok = toImmutableRef(ref)
if !ok {
return errors.Errorf("invalid reference for exporting: %T", ref)
}
if err := immutable.Finalize(ctx); err != nil {
return err
}
}
if exp := req.Exporter; exp != nil {
@ -788,6 +794,10 @@ func (s *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (cache
if !ok {
return nil, nil, errors.Errorf("invalid frontend: %s", req.Frontend)
}
} else {
if req.Definition == nil || req.Definition.Def == nil {
return nil, nil, nil
}
}
ref, exp, err := s.solve(ctx, s.job, SolveRequest{
Definition: req.Definition,