diff --git a/components/engine/daemon/build.go b/components/engine/daemon/build.go index 1bf05ded2c..63006235f7 100644 --- a/components/engine/daemon/build.go +++ b/components/engine/daemon/build.go @@ -1,6 +1,8 @@ package daemon import ( + "io" + "github.com/Sirupsen/logrus" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" @@ -12,7 +14,6 @@ import ( "github.com/docker/docker/registry" "github.com/pkg/errors" "golang.org/x/net/context" - "io" ) type releaseableLayer struct { @@ -104,13 +105,19 @@ func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfi // Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent // leaking of layers. func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) { - if !opts.ForcePull { - image, _ := daemon.GetImage(refOrID) + id, _ := daemon.GetImageID(refOrID) + refIsID := id.String() == refOrID // detect if ref is an ID to skip pulling + + if refIsID || !opts.ForcePull { + image, err := daemon.GetImage(refOrID) // TODO: shouldn't we error out if error is different from "not found" ? if image != nil { layer, err := newReleasableLayerForImage(image, daemon.layerStore) return image, layer, err } + if refIsID { + return nil, nil, err + } } image, err := daemon.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output) diff --git a/components/engine/integration-cli/docker_api_build_test.go b/components/engine/integration-cli/docker_api_build_test.go index 55893a1c75..0544403780 100644 --- a/components/engine/integration-cli/docker_api_build_test.go +++ b/components/engine/integration-cli/docker_api_build_test.go @@ -5,11 +5,13 @@ import ( "bytes" "encoding/json" "fmt" + "io" "io/ioutil" "net/http" "regexp" "strings" + "github.com/docker/docker/api/types" "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli/build/fakecontext" "github.com/docker/docker/integration-cli/cli/build/fakegit" @@ -367,6 +369,44 @@ func (s *DockerSuite) TestBuildAddRemoteNoDecompress(c *check.C) { assert.Contains(c, string(out), "Successfully built") } +func (s *DockerRegistrySuite) TestBuildCopyFromForcePull(c *check.C) { + client, err := request.NewClient() + require.NoError(c, err) + + repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) + // tag the image to upload it to the private registry + err = client.ImageTag(context.TODO(), "busybox", repoName) + assert.Nil(c, err) + // push the image to the registry + rc, err := client.ImagePush(context.TODO(), repoName, types.ImagePushOptions{RegistryAuth: "{}"}) + assert.Nil(c, err) + _, err = io.Copy(ioutil.Discard, rc) + assert.Nil(c, err) + + dockerfile := fmt.Sprintf(` + FROM %s AS foo + RUN touch abc + FROM %s + COPY --from=foo /abc / + `, repoName, repoName) + + ctx := fakecontext.New(c, "", + fakecontext.WithDockerfile(dockerfile), + ) + defer ctx.Close() + + res, body, err := request.Post( + "/build?pull=1", + request.RawContent(ctx.AsTarReader(c)), + request.ContentType("application/x-tar")) + require.NoError(c, err) + assert.Equal(c, http.StatusOK, res.StatusCode) + + out, err := testutil.ReadBody(body) + require.NoError(c, err) + assert.Contains(c, string(out), "Successfully built") +} + type buildLine struct { Stream string Aux struct {