Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
| ... | ... |
@@ -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 |