Browse Source

Add support for `FROM` using named block

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Tonis Tiigi 8 years ago
parent
commit
672ea30a94

+ 30 - 20
builder/dockerfile/dispatchers.go

@@ -227,27 +227,28 @@ func from(b *Builder, args []string, attributes map[string]bool, original string
 		return err
 	}
 
-	// Windows cannot support a container with no base image.
-	if name == api.NoBaseImageSpecifier {
-		if runtime.GOOS == "windows" {
-			return errors.New("Windows does not support FROM scratch")
+	if im, ok := b.imageContexts.byName[name]; ok {
+		if len(im.ImageID()) > 0 {
+			image = im
 		}
-		b.image = ""
-		b.noBaseImage = true
 	} else {
-		// TODO: don't use `name`, instead resolve it to a digest
-		if !b.options.PullParent {
-			image, _ = b.docker.GetImageOnBuild(name)
-			// TODO: shouldn't we error out if error is different from "not found" ?
-		}
-		if image == nil {
+		// Windows cannot support a container with no base image.
+		if name == api.NoBaseImageSpecifier {
+			if runtime.GOOS == "windows" {
+				return errors.New("Windows does not support FROM scratch")
+			}
+			b.image = ""
+			b.noBaseImage = true
+		} else {
 			var err error
-			image, err = b.docker.PullOnBuild(b.clientCtx, name, b.options.AuthConfigs, b.Output)
+			image, err = pullOrGetImage(b, name)
 			if err != nil {
 				return err
 			}
 		}
-		b.imageContexts.update(image.ImageID())
+	}
+	if image != nil {
+		b.imageContexts.update(image.ImageID(), image.RunConfig())
 	}
 	b.from = image
 
@@ -838,9 +839,23 @@ func getShell(c *container.Config) []string {
 
 // mountByRef creates an imageMount from a reference. pulling the image if needed.
 func mountByRef(b *Builder, name string) (*imageMount, error) {
+	image, err := pullOrGetImage(b, name)
+	if err != nil {
+		return nil, err
+	}
+	im, err := b.imageContexts.new("", false)
+	if err != nil {
+		return nil, err
+	}
+	im.id = image.ImageID()
+	return im, nil
+}
+
+func pullOrGetImage(b *Builder, name string) (builder.Image, error) {
 	var image builder.Image
 	if !b.options.PullParent {
 		image, _ = b.docker.GetImageOnBuild(name)
+		// TODO: shouldn't we error out if error is different from "not found" ?
 	}
 	if image == nil {
 		var err error
@@ -849,10 +864,5 @@ func mountByRef(b *Builder, name string) (*imageMount, error) {
 			return nil, err
 		}
 	}
-	im, err := b.imageContexts.new("", false)
-	if err != nil {
-		return nil, err
-	}
-	im.id = image.ImageID()
-	return im, nil
+	return image, nil
 }

+ 15 - 5
builder/dockerfile/imagecontext.go

@@ -6,6 +6,7 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder/remotecontext"
 	"github.com/pkg/errors"
@@ -37,8 +38,9 @@ func (ic *imageContexts) new(name string, increment bool) (*imageMount, error) {
 	return im, nil
 }
 
-func (ic *imageContexts) update(imageID string) {
+func (ic *imageContexts) update(imageID string, runConfig *container.Config) {
 	ic.list[len(ic.list)-1].id = imageID
+	ic.list[len(ic.list)-1].runConfig = runConfig
 }
 
 func (ic *imageContexts) validate(i int) error {
@@ -105,10 +107,11 @@ func (ic *imageContexts) setCache(id, path string, v interface{}) {
 // imageMount is a reference for getting access to a buildcontext that is backed
 // by an existing image
 type imageMount struct {
-	id      string
-	ctx     builder.Context
-	release func() error
-	ic      *imageContexts
+	id        string
+	ctx       builder.Context
+	release   func() error
+	ic        *imageContexts
+	runConfig *container.Config
 }
 
 func (im *imageMount) context() (builder.Context, error) {
@@ -140,6 +143,13 @@ func (im *imageMount) unmount() error {
 	return nil
 }
 
+func (im *imageMount) ImageID() string {
+	return im.id
+}
+func (im *imageMount) RunConfig() *container.Config {
+	return im.runConfig
+}
+
 type pathCache struct {
 	mu    sync.Mutex
 	items map[string]interface{}

+ 2 - 2
builder/dockerfile/internals.go

@@ -85,7 +85,7 @@ func (b *Builder) commit(id string, autoCmd strslice.StrSlice, comment string) e
 	}
 
 	b.image = imageID
-	b.imageContexts.update(imageID)
+	b.imageContexts.update(imageID, &autoConfig)
 	return nil
 }
 
@@ -497,7 +497,7 @@ func (b *Builder) probeCache() (bool, error) {
 	fmt.Fprint(b.Stdout, " ---> Using cache\n")
 	logrus.Debugf("[BUILDER] Use cached version: %s", b.runConfig.Cmd)
 	b.image = string(cache)
-	b.imageContexts.update(b.image)
+	b.imageContexts.update(b.image, b.runConfig)
 
 	return true, nil
 }

+ 61 - 0
integration-cli/docker_cli_build_test.go

@@ -5941,6 +5941,67 @@ func (s *DockerSuite) TestBuildCopyFromImplicitFrom(c *check.C) {
 	}
 }
 
+func (s *DockerRegistrySuite) TestBuildCopyFromImplicitPullingFrom(c *check.C) {
+	repoName := fmt.Sprintf("%v/dockercli/testf", privateRegistryURL)
+
+	dockerfile := `
+		FROM busybox
+		COPY foo bar`
+	ctx := fakeContext(c, dockerfile, map[string]string{
+		"Dockerfile": dockerfile,
+		"foo":        "abc",
+	})
+	defer ctx.Close()
+
+	result := buildImage(repoName, withExternalBuildContext(ctx))
+	result.Assert(c, icmd.Success)
+
+	dockerCmd(c, "push", repoName)
+	dockerCmd(c, "rmi", repoName)
+
+	dockerfile = `
+		FROM busybox
+		COPY --from=%s bar baz`
+
+	ctx = fakeContext(c, fmt.Sprintf(dockerfile, repoName), map[string]string{
+		"Dockerfile": dockerfile,
+	})
+	defer ctx.Close()
+
+	result = buildImage("build1", withExternalBuildContext(ctx))
+	result.Assert(c, icmd.Success)
+
+	dockerCmdWithResult("run", "build1", "cat", "baz").Assert(c, icmd.Expected{Out: "abc"})
+}
+
+func (s *DockerSuite) TestBuildFromPreviousBlock(c *check.C) {
+	dockerfile := `
+		FROM busybox as foo
+		COPY foo /
+		FROM foo as foo1
+		RUN echo 1 >> foo
+		FROM foo as foO2
+		RUN echo 2 >> foo
+		FROM foo
+		COPY --from=foo1 foo f1
+		COPY --from=FOo2 foo f2
+		` // foo2 case also tests that names are canse insensitive
+	ctx := fakeContext(c, dockerfile, map[string]string{
+		"Dockerfile": dockerfile,
+		"foo":        "bar",
+	})
+	defer ctx.Close()
+
+	result := buildImage("build1", withExternalBuildContext(ctx))
+	result.Assert(c, icmd.Success)
+
+	dockerCmdWithResult("run", "build1", "cat", "foo").Assert(c, icmd.Expected{Out: "bar"})
+
+	dockerCmdWithResult("run", "build1", "cat", "f1").Assert(c, icmd.Expected{Out: "bar1"})
+
+	dockerCmdWithResult("run", "build1", "cat", "f2").Assert(c, icmd.Expected{Out: "bar2"})
+}
+
 // TestBuildOpaqueDirectory tests that a build succeeds which
 // creates opaque directories.
 // See https://github.com/docker/docker/issues/25244