Browse code

Add support for `FROM` using named block

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2017/03/23 10:36:08
Showing 4 changed files
... ...
@@ -227,27 +227,28 @@ func from(b *Builder, args []string, attributes map[string]bool, original string
227 227
 		return err
228 228
 	}
229 229
 
230
-	// Windows cannot support a container with no base image.
231
-	if name == api.NoBaseImageSpecifier {
232
-		if runtime.GOOS == "windows" {
233
-			return errors.New("Windows does not support FROM scratch")
230
+	if im, ok := b.imageContexts.byName[name]; ok {
231
+		if len(im.ImageID()) > 0 {
232
+			image = im
234 233
 		}
235
-		b.image = ""
236
-		b.noBaseImage = true
237 234
 	} else {
238
-		// TODO: don't use `name`, instead resolve it to a digest
239
-		if !b.options.PullParent {
240
-			image, _ = b.docker.GetImageOnBuild(name)
241
-			// TODO: shouldn't we error out if error is different from "not found" ?
242
-		}
243
-		if image == nil {
235
+		// Windows cannot support a container with no base image.
236
+		if name == api.NoBaseImageSpecifier {
237
+			if runtime.GOOS == "windows" {
238
+				return errors.New("Windows does not support FROM scratch")
239
+			}
240
+			b.image = ""
241
+			b.noBaseImage = true
242
+		} else {
244 243
 			var err error
245
-			image, err = b.docker.PullOnBuild(b.clientCtx, name, b.options.AuthConfigs, b.Output)
244
+			image, err = pullOrGetImage(b, name)
246 245
 			if err != nil {
247 246
 				return err
248 247
 			}
249 248
 		}
250
-		b.imageContexts.update(image.ImageID())
249
+	}
250
+	if image != nil {
251
+		b.imageContexts.update(image.ImageID(), image.RunConfig())
251 252
 	}
252 253
 	b.from = image
253 254
 
... ...
@@ -838,9 +839,23 @@ func getShell(c *container.Config) []string {
838 838
 
839 839
 // mountByRef creates an imageMount from a reference. pulling the image if needed.
840 840
 func mountByRef(b *Builder, name string) (*imageMount, error) {
841
+	image, err := pullOrGetImage(b, name)
842
+	if err != nil {
843
+		return nil, err
844
+	}
845
+	im, err := b.imageContexts.new("", false)
846
+	if err != nil {
847
+		return nil, err
848
+	}
849
+	im.id = image.ImageID()
850
+	return im, nil
851
+}
852
+
853
+func pullOrGetImage(b *Builder, name string) (builder.Image, error) {
841 854
 	var image builder.Image
842 855
 	if !b.options.PullParent {
843 856
 		image, _ = b.docker.GetImageOnBuild(name)
857
+		// TODO: shouldn't we error out if error is different from "not found" ?
844 858
 	}
845 859
 	if image == nil {
846 860
 		var err error
... ...
@@ -849,10 +864,5 @@ func mountByRef(b *Builder, name string) (*imageMount, error) {
849 849
 			return nil, err
850 850
 		}
851 851
 	}
852
-	im, err := b.imageContexts.new("", false)
853
-	if err != nil {
854
-		return nil, err
855
-	}
856
-	im.id = image.ImageID()
857
-	return im, nil
852
+	return image, nil
858 853
 }
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"sync"
7 7
 
8 8
 	"github.com/Sirupsen/logrus"
9
+	"github.com/docker/docker/api/types/container"
9 10
 	"github.com/docker/docker/builder"
10 11
 	"github.com/docker/docker/builder/remotecontext"
11 12
 	"github.com/pkg/errors"
... ...
@@ -37,8 +38,9 @@ func (ic *imageContexts) new(name string, increment bool) (*imageMount, error) {
37 37
 	return im, nil
38 38
 }
39 39
 
40
-func (ic *imageContexts) update(imageID string) {
40
+func (ic *imageContexts) update(imageID string, runConfig *container.Config) {
41 41
 	ic.list[len(ic.list)-1].id = imageID
42
+	ic.list[len(ic.list)-1].runConfig = runConfig
42 43
 }
43 44
 
44 45
 func (ic *imageContexts) validate(i int) error {
... ...
@@ -105,10 +107,11 @@ func (ic *imageContexts) setCache(id, path string, v interface{}) {
105 105
 // imageMount is a reference for getting access to a buildcontext that is backed
106 106
 // by an existing image
107 107
 type imageMount struct {
108
-	id      string
109
-	ctx     builder.Context
110
-	release func() error
111
-	ic      *imageContexts
108
+	id        string
109
+	ctx       builder.Context
110
+	release   func() error
111
+	ic        *imageContexts
112
+	runConfig *container.Config
112 113
 }
113 114
 
114 115
 func (im *imageMount) context() (builder.Context, error) {
... ...
@@ -140,6 +143,13 @@ func (im *imageMount) unmount() error {
140 140
 	return nil
141 141
 }
142 142
 
143
+func (im *imageMount) ImageID() string {
144
+	return im.id
145
+}
146
+func (im *imageMount) RunConfig() *container.Config {
147
+	return im.runConfig
148
+}
149
+
143 150
 type pathCache struct {
144 151
 	mu    sync.Mutex
145 152
 	items map[string]interface{}
... ...
@@ -85,7 +85,7 @@ func (b *Builder) commit(id string, autoCmd strslice.StrSlice, comment string) e
85 85
 	}
86 86
 
87 87
 	b.image = imageID
88
-	b.imageContexts.update(imageID)
88
+	b.imageContexts.update(imageID, &autoConfig)
89 89
 	return nil
90 90
 }
91 91
 
... ...
@@ -497,7 +497,7 @@ func (b *Builder) probeCache() (bool, error) {
497 497
 	fmt.Fprint(b.Stdout, " ---> Using cache\n")
498 498
 	logrus.Debugf("[BUILDER] Use cached version: %s", b.runConfig.Cmd)
499 499
 	b.image = string(cache)
500
-	b.imageContexts.update(b.image)
500
+	b.imageContexts.update(b.image, b.runConfig)
501 501
 
502 502
 	return true, nil
503 503
 }
... ...
@@ -5941,6 +5941,67 @@ func (s *DockerSuite) TestBuildCopyFromImplicitFrom(c *check.C) {
5941 5941
 	}
5942 5942
 }
5943 5943
 
5944
+func (s *DockerRegistrySuite) TestBuildCopyFromImplicitPullingFrom(c *check.C) {
5945
+	repoName := fmt.Sprintf("%v/dockercli/testf", privateRegistryURL)
5946
+
5947
+	dockerfile := `
5948
+		FROM busybox
5949
+		COPY foo bar`
5950
+	ctx := fakeContext(c, dockerfile, map[string]string{
5951
+		"Dockerfile": dockerfile,
5952
+		"foo":        "abc",
5953
+	})
5954
+	defer ctx.Close()
5955
+
5956
+	result := buildImage(repoName, withExternalBuildContext(ctx))
5957
+	result.Assert(c, icmd.Success)
5958
+
5959
+	dockerCmd(c, "push", repoName)
5960
+	dockerCmd(c, "rmi", repoName)
5961
+
5962
+	dockerfile = `
5963
+		FROM busybox
5964
+		COPY --from=%s bar baz`
5965
+
5966
+	ctx = fakeContext(c, fmt.Sprintf(dockerfile, repoName), map[string]string{
5967
+		"Dockerfile": dockerfile,
5968
+	})
5969
+	defer ctx.Close()
5970
+
5971
+	result = buildImage("build1", withExternalBuildContext(ctx))
5972
+	result.Assert(c, icmd.Success)
5973
+
5974
+	dockerCmdWithResult("run", "build1", "cat", "baz").Assert(c, icmd.Expected{Out: "abc"})
5975
+}
5976
+
5977
+func (s *DockerSuite) TestBuildFromPreviousBlock(c *check.C) {
5978
+	dockerfile := `
5979
+		FROM busybox as foo
5980
+		COPY foo /
5981
+		FROM foo as foo1
5982
+		RUN echo 1 >> foo
5983
+		FROM foo as foO2
5984
+		RUN echo 2 >> foo
5985
+		FROM foo
5986
+		COPY --from=foo1 foo f1
5987
+		COPY --from=FOo2 foo f2
5988
+		` // foo2 case also tests that names are canse insensitive
5989
+	ctx := fakeContext(c, dockerfile, map[string]string{
5990
+		"Dockerfile": dockerfile,
5991
+		"foo":        "bar",
5992
+	})
5993
+	defer ctx.Close()
5994
+
5995
+	result := buildImage("build1", withExternalBuildContext(ctx))
5996
+	result.Assert(c, icmd.Success)
5997
+
5998
+	dockerCmdWithResult("run", "build1", "cat", "foo").Assert(c, icmd.Expected{Out: "bar"})
5999
+
6000
+	dockerCmdWithResult("run", "build1", "cat", "f1").Assert(c, icmd.Expected{Out: "bar1"})
6001
+
6002
+	dockerCmdWithResult("run", "build1", "cat", "f2").Assert(c, icmd.Expected{Out: "bar2"})
6003
+}
6004
+
5944 6005
 // TestBuildOpaqueDirectory tests that a build succeeds which
5945 6006
 // creates opaque directories.
5946 6007
 // See https://github.com/docker/docker/issues/25244