summaryrefslogtreecommitdiff
path: root/vendor/github.com/projectatomic/buildah/imagebuildah/build.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/projectatomic/buildah/imagebuildah/build.go')
-rw-r--r--vendor/github.com/projectatomic/buildah/imagebuildah/build.go119
1 files changed, 106 insertions, 13 deletions
diff --git a/vendor/github.com/projectatomic/buildah/imagebuildah/build.go b/vendor/github.com/projectatomic/buildah/imagebuildah/build.go
index 672d6e94d..42e51878e 100644
--- a/vendor/github.com/projectatomic/buildah/imagebuildah/build.go
+++ b/vendor/github.com/projectatomic/buildah/imagebuildah/build.go
@@ -1,11 +1,14 @@
package imagebuildah
import (
+ "bytes"
"context"
"fmt"
"io"
+ "io/ioutil"
"net/http"
"os"
+ "os/exec"
"path/filepath"
"strconv"
"strings"
@@ -215,6 +218,7 @@ type Executor struct {
noCache bool
removeIntermediateCtrs bool
forceRmIntermediateCtrs bool
+ containerIDs []string // Stores the IDs of the successful intermediate containers used during layer build
}
// withName creates a new child executor that will be used whenever a COPY statement uses --from=NAME.
@@ -684,6 +688,7 @@ func (b *Executor) Prepare(ctx context.Context, ib *imagebuilder.Builder, node *
// Add the top layer of this image to b.topLayers so we can keep track of them
// when building with cached images.
b.topLayers = append(b.topLayers, builder.TopLayer)
+ logrus.Debugln("Container ID:", builder.ContainerID)
return nil
}
@@ -811,12 +816,8 @@ func (b *Executor) Execute(ctx context.Context, ib *imagebuilder.Builder, node *
// it is used to create the container for the next step.
imgID = cacheID
}
- // Delete the intermediate container if b.removeIntermediateCtrs is true.
- if b.removeIntermediateCtrs {
- if err := b.Delete(); err != nil {
- return errors.Wrap(err, "error deleting intermediate container")
- }
- }
+ // Add container ID of successful intermediate container to b.containerIDs
+ b.containerIDs = append(b.containerIDs, b.builder.ContainerID)
// Prepare for the next step with imgID as the new base image.
if i != len(children)-1 {
if err := b.Prepare(ctx, ib, node, imgID); err != nil {
@@ -1122,11 +1123,14 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) error
if len(stages) == 0 {
errors.New("error building: no stages to build")
}
- var stageExecutor *Executor
+ var (
+ stageExecutor *Executor
+ lastErr error
+ )
for _, stage := range stages {
stageExecutor = b.withName(stage.Name, stage.Position)
if err := stageExecutor.Prepare(ctx, stage.Builder, stage.Node, ""); err != nil {
- return err
+ lastErr = err
}
// Always remove the intermediate/build containers, even if the build was unsuccessful.
// If building with layers, remove all intermediate/build containers if b.forceRmIntermediateCtrs
@@ -1135,8 +1139,18 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) error
defer stageExecutor.Delete()
}
if err := stageExecutor.Execute(ctx, stage.Builder, stage.Node); err != nil {
- return err
+ lastErr = err
}
+
+ // Delete the successful intermediate containers if an error in the build
+ // process occurs and b.removeIntermediateCtrs is true.
+ if lastErr != nil {
+ if b.removeIntermediateCtrs {
+ stageExecutor.deleteSuccessfulIntermediateCtrs()
+ }
+ return lastErr
+ }
+ b.containerIDs = append(b.containerIDs, stageExecutor.containerIDs...)
}
if !b.layers && !b.noCache {
@@ -1154,7 +1168,9 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) error
// the removal of intermediate/build containers will be handled by the
// defer statement above.
if b.removeIntermediateCtrs && (b.layers || b.noCache) {
- return stageExecutor.Delete()
+ if err := b.deleteSuccessfulIntermediateCtrs(); err != nil {
+ return errors.Errorf("Failed to cleanup intermediate containers")
+ }
}
return nil
}
@@ -1173,6 +1189,8 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOpt
}
}(dockerfiles...)
for _, dfile := range paths {
+ var data io.ReadCloser
+
if strings.HasPrefix(dfile, "http://") || strings.HasPrefix(dfile, "https://") {
logrus.Debugf("reading remote Dockerfile %q", dfile)
resp, err := http.Get(dfile)
@@ -1183,7 +1201,7 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOpt
resp.Body.Close()
return errors.Errorf("no contents in %q", dfile)
}
- dockerfiles = append(dockerfiles, resp.Body)
+ data = resp.Body
} else {
if !filepath.IsAbs(dfile) {
logrus.Debugf("resolving local Dockerfile %q", dfile)
@@ -1199,12 +1217,23 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOpt
contents.Close()
return errors.Wrapf(err, "error reading info about %q", dfile)
}
- if dinfo.Size() == 0 {
+ if dinfo.Mode().IsRegular() && dinfo.Size() == 0 {
contents.Close()
return errors.Wrapf(err, "no contents in %q", dfile)
}
- dockerfiles = append(dockerfiles, contents)
+ data = contents
+ }
+
+ // pre-process Dockerfiles with ".in" suffix
+ if strings.HasSuffix(dfile, ".in") {
+ pData, err := preprocessDockerfileContents(data, options.ContextDirectory)
+ if err != nil {
+ return err
+ }
+ data = *pData
}
+
+ dockerfiles = append(dockerfiles, data)
}
mainNode, err := imagebuilder.ParseDockerfile(dockerfiles[0])
if err != nil {
@@ -1225,3 +1254,67 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOpt
stages := imagebuilder.NewStages(mainNode, b)
return exec.Build(ctx, stages)
}
+
+// deleteSuccessfulIntermediateCtrs goes through the container IDs in b.containerIDs
+// and deletes the containers associated with that ID.
+func (b *Executor) deleteSuccessfulIntermediateCtrs() error {
+ var lastErr error
+ for _, ctr := range b.containerIDs {
+ if err := b.store.DeleteContainer(ctr); err != nil {
+ logrus.Errorf("error deleting build container %q: %v\n", ctr, err)
+ lastErr = err
+ }
+ }
+ return lastErr
+}
+
+// preprocessDockerfileContents runs CPP(1) in preprocess-only mode on the input
+// dockerfile content and will use ctxDir as the base include path.
+//
+// Note: we cannot use cmd.StdoutPipe() as cmd.Wait() closes it.
+func preprocessDockerfileContents(r io.ReadCloser, ctxDir string) (rdrCloser *io.ReadCloser, err error) {
+ cppPath := "/usr/bin/cpp"
+ if _, err = os.Stat(cppPath); err != nil {
+ if os.IsNotExist(err) {
+ err = errors.Errorf("error: Dockerfile.in support requires %s to be installed", cppPath)
+ }
+ return nil, err
+ }
+
+ stdout := bytes.Buffer{}
+ stderr := bytes.Buffer{}
+
+ cmd := exec.Command(cppPath, "-E", "-iquote", ctxDir, "-")
+ cmd.Stdout = &stdout
+ cmd.Stderr = &stderr
+
+ pipe, err := cmd.StdinPipe()
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if err != nil {
+ pipe.Close()
+ }
+ }()
+
+ if err = cmd.Start(); err != nil {
+ return nil, err
+ }
+
+ if _, err = io.Copy(pipe, r); err != nil {
+ return nil, err
+ }
+
+ pipe.Close()
+ if err = cmd.Wait(); err != nil {
+ if stderr.Len() > 0 {
+ err = fmt.Errorf("%v: %s", err, strings.TrimSpace(stderr.String()))
+ }
+ return nil, errors.Wrapf(err, "error pre-processing Dockerfile")
+ }
+
+ rc := ioutil.NopCloser(bytes.NewReader(stdout.Bytes()))
+ return &rc, nil
+}