|
@@ -12,7 +12,6 @@ import (
|
|
|
"github.com/docker/docker/api/types"
|
|
|
"github.com/docker/docker/api/types/backend"
|
|
|
"github.com/docker/docker/api/types/container"
|
|
|
- "github.com/docker/docker/builder"
|
|
|
"github.com/docker/docker/image"
|
|
|
"github.com/docker/docker/pkg/stringid"
|
|
|
"github.com/pkg/errors"
|
|
@@ -65,14 +64,44 @@ func (b *Builder) commitContainer(dispatchState *dispatchState, id string, conta
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (b *Builder) exportImage(state *dispatchState, image builder.Image) error {
|
|
|
- config, err := image.MarshalJSON()
|
|
|
+func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runConfig *container.Config) error {
|
|
|
+ newLayer, err := imageMount.Layer().Commit()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ // add an image mount without an image so the layer is properly unmounted
|
|
|
+ // if there is an error before we can add the full mount with image
|
|
|
+ b.imageSources.Add(newImageMount(nil, newLayer))
|
|
|
+
|
|
|
+ parentImage, ok := imageMount.Image().(*image.Image)
|
|
|
+ if !ok {
|
|
|
+ return errors.Errorf("unexpected image type")
|
|
|
+ }
|
|
|
+
|
|
|
+ newImage := image.NewChildImage(parentImage, image.ChildConfig{
|
|
|
+ Author: state.maintainer,
|
|
|
+ ContainerConfig: runConfig,
|
|
|
+ DiffID: newLayer.DiffID(),
|
|
|
+ Config: copyRunConfig(state.runConfig),
|
|
|
+ })
|
|
|
+
|
|
|
+ // TODO: it seems strange to marshal this here instead of just passing in the
|
|
|
+ // image struct
|
|
|
+ config, err := newImage.MarshalJSON()
|
|
|
if err != nil {
|
|
|
return errors.Wrap(err, "failed to encode image config")
|
|
|
}
|
|
|
|
|
|
- state.imageID, err = b.docker.CreateImage(config, state.imageID)
|
|
|
- return err
|
|
|
+ exportedImage, err := b.docker.CreateImage(config, state.imageID)
|
|
|
+ if err != nil {
|
|
|
+ return errors.Wrapf(err, "failed to export image")
|
|
|
+ }
|
|
|
+
|
|
|
+ state.imageID = exportedImage.ImageID()
|
|
|
+ b.imageSources.Add(newImageMount(exportedImage, newLayer))
|
|
|
+ b.buildStages.update(state.imageID)
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error {
|
|
@@ -82,46 +111,46 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
|
|
|
runConfigWithCommentCmd := copyRunConfig(
|
|
|
state.runConfig,
|
|
|
withCmdCommentString(fmt.Sprintf("%s %s in %s ", inst.cmdName, srcHash, inst.dest)))
|
|
|
- containerID, err := b.probeAndCreate(state, runConfigWithCommentCmd)
|
|
|
- if err != nil || containerID == "" {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // Twiddle the destination when it's a relative path - meaning, make it
|
|
|
- // relative to the WORKINGDIR
|
|
|
- dest, err := normaliseDest(inst.cmdName, state.runConfig.WorkingDir, inst.dest)
|
|
|
- if err != nil {
|
|
|
+ hit, err := b.probeCache(state, runConfigWithCommentCmd)
|
|
|
+ if err != nil || hit {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
imageMount, err := b.imageSources.Get(state.imageID)
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
|
|
|
}
|
|
|
- destSource, err := imageMount.Source()
|
|
|
+ destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, imageMount)
|
|
|
if err != nil {
|
|
|
- return errors.Wrapf(err, "failed to mount copy source")
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- destInfo := newCopyInfoFromSource(destSource, dest, "")
|
|
|
opts := copyFileOptions{
|
|
|
decompress: inst.allowLocalDecompression,
|
|
|
archiver: b.archiver,
|
|
|
}
|
|
|
for _, info := range inst.infos {
|
|
|
- if err := copyFile(destInfo, info, opts); err != nil {
|
|
|
- return err
|
|
|
+ if err := performCopyForInfo(destInfo, info, opts); err != nil {
|
|
|
+ return errors.Wrapf(err, "failed to copy files")
|
|
|
}
|
|
|
}
|
|
|
+ return b.exportImage(state, imageMount, runConfigWithCommentCmd)
|
|
|
+}
|
|
|
|
|
|
- newImage := imageMount.Image().NewChild(image.ChildConfig{
|
|
|
- Author: state.maintainer,
|
|
|
- DiffID: imageMount.DiffID(),
|
|
|
- ContainerConfig: runConfigWithCommentCmd,
|
|
|
- // TODO: ContainerID?
|
|
|
- // TODO: Config?
|
|
|
- })
|
|
|
- return b.exportImage(state, newImage)
|
|
|
+func createDestInfo(workingDir string, inst copyInstruction, imageMount *imageMount) (copyInfo, error) {
|
|
|
+ // Twiddle the destination when it's a relative path - meaning, make it
|
|
|
+ // relative to the WORKINGDIR
|
|
|
+ dest, err := normaliseDest(workingDir, inst.dest)
|
|
|
+ if err != nil {
|
|
|
+ return copyInfo{}, errors.Wrapf(err, "invalid %s", inst.cmdName)
|
|
|
+ }
|
|
|
+
|
|
|
+ destMount, err := imageMount.Source()
|
|
|
+ if err != nil {
|
|
|
+ return copyInfo{}, errors.Wrapf(err, "failed to mount copy source")
|
|
|
+ }
|
|
|
+
|
|
|
+ return newCopyInfoFromSource(destMount, dest, ""), nil
|
|
|
}
|
|
|
|
|
|
// For backwards compat, if there's just one info then use it as the
|