2017-03-28 15:06:13 +08:00
|
|
|
package imagebuildah
|
|
|
|
|
|
|
|
import (
|
2018-08-01 18:31:02 +08:00
|
|
|
"bytes"
|
2018-04-12 22:20:36 +08:00
|
|
|
"context"
|
2017-03-28 15:06:13 +08:00
|
|
|
"io"
|
2018-08-01 18:31:02 +08:00
|
|
|
"io/ioutil"
|
2017-03-28 15:06:13 +08:00
|
|
|
"net/http"
|
|
|
|
"os"
|
2018-08-01 18:31:02 +08:00
|
|
|
"os/exec"
|
2017-03-28 15:06:13 +08:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2019-11-28 00:31:02 +08:00
|
|
|
"time"
|
2017-03-28 15:06:13 +08:00
|
|
|
|
2018-09-18 03:20:16 +08:00
|
|
|
"github.com/containers/buildah"
|
2020-02-08 01:54:18 +08:00
|
|
|
"github.com/containers/common/pkg/config"
|
2019-10-26 05:19:30 +08:00
|
|
|
"github.com/containers/image/v5/docker/reference"
|
|
|
|
"github.com/containers/image/v5/types"
|
2017-05-17 23:53:28 +08:00
|
|
|
"github.com/containers/storage"
|
2017-03-28 15:06:13 +08:00
|
|
|
"github.com/containers/storage/pkg/archive"
|
2019-10-02 04:03:57 +08:00
|
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
2017-03-28 15:06:13 +08:00
|
|
|
"github.com/openshift/imagebuilder"
|
2017-06-02 03:23:02 +08:00
|
|
|
"github.com/pkg/errors"
|
2017-10-10 03:05:56 +08:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-03-28 15:06:13 +08:00
|
|
|
)
|
|
|
|
|
2017-04-11 02:15:30 +08:00
|
|
|
const (
|
2018-08-24 00:55:16 +08:00
|
|
|
PullIfMissing = buildah.PullIfMissing
|
|
|
|
PullAlways = buildah.PullAlways
|
Fix --pull=true||false and add --pull-never to bud and from (retry)
(Replaces #1873 as it had lint issues that were timing out tests that I couldn't
track down easily)
Prior to this fix, if someone did `buildah bud --pull=false .` and the image in
the Containerfile's FROM statement was not local, the build would fail. The same
build on Docker will succeed. In Docker, when `--pull` is set to false, it only
pulls the image from the registry if there was not one locally. Buildah would never
pull the image and if the image was not locally available, it would throw an error.
In certain Kubernetes environments, this was especially troublesome.
To retain the old `--pull=false` functionality, I've created a new `--pull-never`
option that fails if an image is not locally available just like the old
`--pull=false` option used to do.
In addition, if there was a newer version of the image on the repository than
the one locally, the `--pull=true` option would not pull the image as it should
have, this corrects that.
Changes both the from and bud commands.
Addresses: #1675
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
Closes: #1959
Approved by: rhatdan
2019-10-31 22:15:56 +08:00
|
|
|
PullIfNewer = buildah.PullIfNewer
|
2018-08-24 00:55:16 +08:00
|
|
|
PullNever = buildah.PullNever
|
2017-04-11 22:27:05 +08:00
|
|
|
|
|
|
|
Gzip = archive.Gzip
|
|
|
|
Bzip2 = archive.Bzip2
|
|
|
|
Xz = archive.Xz
|
2019-07-09 05:50:33 +08:00
|
|
|
Zstd = archive.Zstd
|
2017-04-11 22:27:05 +08:00
|
|
|
Uncompressed = archive.Uncompressed
|
2017-04-11 02:15:30 +08:00
|
|
|
)
|
|
|
|
|
2017-04-11 22:27:05 +08:00
|
|
|
// Mount is a mountpoint for the build container.
|
|
|
|
type Mount specs.Mount
|
|
|
|
|
2017-03-28 15:06:13 +08:00
|
|
|
// BuildOptions can be used to alter how an image is built.
|
|
|
|
type BuildOptions struct {
|
|
|
|
// ContextDirectory is the default source location for COPY and ADD
|
|
|
|
// commands.
|
|
|
|
ContextDirectory string
|
2017-04-11 02:15:30 +08:00
|
|
|
// PullPolicy controls whether or not we pull images. It should be one
|
Fix --pull=true||false and add --pull-never to bud and from (retry)
(Replaces #1873 as it had lint issues that were timing out tests that I couldn't
track down easily)
Prior to this fix, if someone did `buildah bud --pull=false .` and the image in
the Containerfile's FROM statement was not local, the build would fail. The same
build on Docker will succeed. In Docker, when `--pull` is set to false, it only
pulls the image from the registry if there was not one locally. Buildah would never
pull the image and if the image was not locally available, it would throw an error.
In certain Kubernetes environments, this was especially troublesome.
To retain the old `--pull=false` functionality, I've created a new `--pull-never`
option that fails if an image is not locally available just like the old
`--pull=false` option used to do.
In addition, if there was a newer version of the image on the repository than
the one locally, the `--pull=true` option would not pull the image as it should
have, this corrects that.
Changes both the from and bud commands.
Addresses: #1675
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
Closes: #1959
Approved by: rhatdan
2019-10-31 22:15:56 +08:00
|
|
|
// of PullIfMissing, PullAlways, PullIfNewer, or PullNever.
|
2018-03-29 02:14:57 +08:00
|
|
|
PullPolicy buildah.PullPolicy
|
2017-03-28 15:06:13 +08:00
|
|
|
// Registry is a value which is prepended to the image's name, if it
|
|
|
|
// needs to be pulled and the image name alone can not be resolved to a
|
2017-07-31 22:44:21 +08:00
|
|
|
// reference to a source image. No separator is implicitly added.
|
2017-03-28 15:06:13 +08:00
|
|
|
Registry string
|
|
|
|
// IgnoreUnrecognizedInstructions tells us to just log instructions we
|
|
|
|
// don't recognize, and try to keep going.
|
|
|
|
IgnoreUnrecognizedInstructions bool
|
|
|
|
// Quiet tells us whether or not to announce steps as we go through them.
|
|
|
|
Quiet bool
|
2018-05-12 01:00:14 +08:00
|
|
|
// Isolation controls how Run() runs things.
|
|
|
|
Isolation buildah.Isolation
|
|
|
|
// Runtime is the name of the command to run for RUN instructions when
|
|
|
|
// Isolation is either IsolationDefault or IsolationOCI. It should
|
|
|
|
// accept the same arguments and flags that runc does.
|
2017-03-28 15:06:13 +08:00
|
|
|
Runtime string
|
|
|
|
// RuntimeArgs adds global arguments for the runtime.
|
|
|
|
RuntimeArgs []string
|
|
|
|
// TransientMounts is a list of mounts that won't be kept in the image.
|
2020-02-08 01:54:18 +08:00
|
|
|
TransientMounts []string
|
2017-03-28 15:06:13 +08:00
|
|
|
// Compression specifies the type of compression which is applied to
|
|
|
|
// layer blobs. The default is to not use compression, but
|
|
|
|
// archive.Gzip is recommended.
|
|
|
|
Compression archive.Compression
|
|
|
|
// Arguments which can be interpolated into Dockerfiles
|
|
|
|
Args map[string]string
|
|
|
|
// Name of the image to write to.
|
|
|
|
Output string
|
2017-04-11 02:25:07 +08:00
|
|
|
// Additional tags to add to the image that we write, if we know of a
|
|
|
|
// way to add them.
|
|
|
|
AdditionalTags []string
|
2017-04-11 02:17:15 +08:00
|
|
|
// Log is a callback that will print a progress message. If no value
|
|
|
|
// is supplied, the message will be sent to Err (or os.Stderr, if Err
|
|
|
|
// is nil) by default.
|
|
|
|
Log func(format string, args ...interface{})
|
2018-08-10 00:44:30 +08:00
|
|
|
// In is connected to stdin for RUN instructions.
|
|
|
|
In io.Reader
|
2017-04-11 02:17:15 +08:00
|
|
|
// Out is a place where non-error log messages are sent.
|
|
|
|
Out io.Writer
|
|
|
|
// Err is a place where error log messages should be sent.
|
|
|
|
Err io.Writer
|
2017-03-28 15:06:13 +08:00
|
|
|
// SignaturePolicyPath specifies an override location for the signature
|
|
|
|
// policy which should be used for verifying the new image as it is
|
|
|
|
// being written. Except in specific circumstances, no value should be
|
|
|
|
// specified, indicating that the shared, system-wide default policy
|
|
|
|
// should be used.
|
|
|
|
SignaturePolicyPath string
|
2017-05-09 23:56:44 +08:00
|
|
|
// ReportWriter is an io.Writer which will be used to report the
|
|
|
|
// progress of the (possible) pulling of the source image and the
|
|
|
|
// writing of the new image.
|
|
|
|
ReportWriter io.Writer
|
2017-05-18 05:02:40 +08:00
|
|
|
// OutputFormat is the format of the output image's manifest and
|
|
|
|
// configuration data.
|
2018-08-24 00:55:16 +08:00
|
|
|
// Accepted values are buildah.OCIv1ImageManifest and buildah.Dockerv2ImageManifest.
|
2017-05-18 05:02:40 +08:00
|
|
|
OutputFormat string
|
2018-01-28 07:17:05 +08:00
|
|
|
// SystemContext holds parameters used for authentication.
|
2018-03-13 01:53:12 +08:00
|
|
|
SystemContext *types.SystemContext
|
|
|
|
// NamespaceOptions controls how we set up namespaces processes that we
|
|
|
|
// might need when handling RUN instructions.
|
|
|
|
NamespaceOptions []buildah.NamespaceOption
|
2018-04-14 06:20:25 +08:00
|
|
|
// ConfigureNetwork controls whether or not network interfaces and
|
|
|
|
// routing are configured for a new network namespace (i.e., when not
|
|
|
|
// joining another's namespace and not just using the host's
|
|
|
|
// namespace), effectively deciding whether or not the process has a
|
|
|
|
// usable network.
|
|
|
|
ConfigureNetwork buildah.NetworkConfigurationPolicy
|
|
|
|
// CNIPluginPath is the location of CNI plugin helpers, if they should be
|
|
|
|
// run from a location other than the default location.
|
|
|
|
CNIPluginPath string
|
|
|
|
// CNIConfigDir is the location of CNI configuration files, if the files in
|
|
|
|
// the default configuration directory shouldn't be used.
|
|
|
|
CNIConfigDir string
|
2018-03-13 01:53:12 +08:00
|
|
|
// ID mapping options to use if we're setting up our own user namespace
|
|
|
|
// when handling RUN instructions.
|
|
|
|
IDMappingOptions *buildah.IDMappingOptions
|
2020-02-08 01:54:18 +08:00
|
|
|
// AddCapabilities is a list of capabilities to add to the default set when
|
2018-06-05 05:36:26 +08:00
|
|
|
// handling RUN instructions.
|
2020-02-08 01:54:18 +08:00
|
|
|
AddCapabilities []string
|
|
|
|
// DropCapabilities is a list of capabilities to remove from the default set
|
|
|
|
// when handling RUN instructions. If a capability appears in both lists, it
|
|
|
|
// will be dropped.
|
|
|
|
DropCapabilities []string
|
|
|
|
CommonBuildOpts *buildah.CommonBuildOptions
|
2018-02-24 01:38:39 +08:00
|
|
|
// DefaultMountsFilePath is the file path holding the mounts to be mounted in "host-path:container-path" format
|
|
|
|
DefaultMountsFilePath string
|
2018-04-25 22:00:46 +08:00
|
|
|
// IIDFile tells the builder to write the image ID to the specified file
|
|
|
|
IIDFile string
|
2018-05-22 05:02:50 +08:00
|
|
|
// Squash tells the builder to produce an image with a single layer
|
|
|
|
// instead of with possibly more than one layer.
|
|
|
|
Squash bool
|
2018-05-17 02:13:12 +08:00
|
|
|
// Labels metadata for an image
|
|
|
|
Labels []string
|
2018-05-23 00:05:18 +08:00
|
|
|
// Annotation metadata for an image
|
|
|
|
Annotations []string
|
2018-05-25 15:53:30 +08:00
|
|
|
// OnBuild commands to be run by images based on this image
|
|
|
|
OnBuild []string
|
2018-06-09 00:55:46 +08:00
|
|
|
// Layers tells the builder to create a cache of images for each step in the Dockerfile
|
|
|
|
Layers bool
|
|
|
|
// NoCache tells the builder to build the image from scratch without checking for a cache.
|
|
|
|
// It creates a new set of cached images for the build.
|
|
|
|
NoCache bool
|
2018-06-14 02:09:45 +08:00
|
|
|
// RemoveIntermediateCtrs tells the builder whether to remove intermediate containers used
|
|
|
|
// during the build process. Default is true.
|
|
|
|
RemoveIntermediateCtrs bool
|
|
|
|
// ForceRmIntermediateCtrs tells the builder to remove all intermediate containers even if
|
|
|
|
// the build was unsuccessful.
|
|
|
|
ForceRmIntermediateCtrs bool
|
2018-10-18 06:06:16 +08:00
|
|
|
// BlobDirectory is a directory which we'll use for caching layer blobs.
|
|
|
|
BlobDirectory string
|
2020-01-16 01:23:38 +08:00
|
|
|
// Target the targeted FROM in the Dockerfile to build.
|
2019-02-03 07:31:44 +08:00
|
|
|
Target string
|
2020-01-16 01:23:38 +08:00
|
|
|
// Devices are the additional devices to add to the containers.
|
2020-02-08 01:54:18 +08:00
|
|
|
Devices []string
|
2020-01-16 01:23:38 +08:00
|
|
|
// SignBy is the fingerprint of a GPG key to use for signing images.
|
|
|
|
SignBy string
|
2020-01-20 19:31:09 +08:00
|
|
|
// Architecture specifies the target architecture of the image to be built.
|
|
|
|
Architecture string
|
|
|
|
// OS is the specifies the operating system of the image to be built.
|
|
|
|
OS string
|
2019-11-28 00:31:02 +08:00
|
|
|
// MaxPullPushRetries is the maximum number of attempts we'll make to pull or push any one
|
|
|
|
// image from or to an external registry if the first attempt fails.
|
|
|
|
MaxPullPushRetries int
|
|
|
|
// PullPushRetryDelay is how long to wait before retrying a pull or push attempt.
|
|
|
|
PullPushRetryDelay time.Duration
|
2017-03-28 15:06:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// BuildDockerfiles parses a set of one or more Dockerfiles (which may be
|
|
|
|
// URLs), creates a new Executor, and then runs Prepare/Execute/Commit/Delete
|
|
|
|
// over the entire set of instructions.
|
2018-10-12 04:58:04 +08:00
|
|
|
func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOptions, paths ...string) (string, reference.Canonical, error) {
|
2018-04-11 01:35:03 +08:00
|
|
|
if len(paths) == 0 {
|
2018-10-12 04:58:04 +08:00
|
|
|
return "", nil, errors.Errorf("error building: no dockerfiles specified")
|
2017-03-28 15:06:13 +08:00
|
|
|
}
|
2018-02-25 06:40:44 +08:00
|
|
|
var dockerfiles []io.ReadCloser
|
|
|
|
defer func(dockerfiles ...io.ReadCloser) {
|
|
|
|
for _, d := range dockerfiles {
|
|
|
|
d.Close()
|
|
|
|
}
|
|
|
|
}(dockerfiles...)
|
2018-09-22 07:37:02 +08:00
|
|
|
|
2018-04-11 01:35:03 +08:00
|
|
|
for _, dfile := range paths {
|
2018-08-01 18:31:02 +08:00
|
|
|
var data io.ReadCloser
|
|
|
|
|
2017-03-28 15:06:13 +08:00
|
|
|
if strings.HasPrefix(dfile, "http://") || strings.HasPrefix(dfile, "https://") {
|
|
|
|
logrus.Debugf("reading remote Dockerfile %q", dfile)
|
|
|
|
resp, err := http.Get(dfile)
|
|
|
|
if err != nil {
|
2018-10-12 04:58:04 +08:00
|
|
|
return "", nil, errors.Wrapf(err, "error getting %q", dfile)
|
2017-03-28 15:06:13 +08:00
|
|
|
}
|
|
|
|
if resp.ContentLength == 0 {
|
|
|
|
resp.Body.Close()
|
2018-10-12 04:58:04 +08:00
|
|
|
return "", nil, errors.Errorf("no contents in %q", dfile)
|
2017-03-28 15:06:13 +08:00
|
|
|
}
|
2018-08-01 18:31:02 +08:00
|
|
|
data = resp.Body
|
2017-03-28 15:06:13 +08:00
|
|
|
} else {
|
2018-08-03 07:48:44 +08:00
|
|
|
// If the Dockerfile isn't found try prepending the
|
|
|
|
// context directory to it.
|
2018-10-28 03:47:03 +08:00
|
|
|
dinfo, err := os.Stat(dfile)
|
|
|
|
if os.IsNotExist(err) {
|
2017-03-28 15:06:13 +08:00
|
|
|
dfile = filepath.Join(options.ContextDirectory, dfile)
|
2019-06-18 19:04:18 +08:00
|
|
|
dinfo, err = os.Stat(dfile)
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, errors.Wrapf(err, "error reading info about %q", dfile)
|
|
|
|
}
|
2018-10-28 03:47:03 +08:00
|
|
|
}
|
|
|
|
// If given a directory, add '/Dockerfile' to it.
|
|
|
|
if dinfo.Mode().IsDir() {
|
|
|
|
dfile = filepath.Join(dfile, "Dockerfile")
|
|
|
|
}
|
2017-03-28 15:06:13 +08:00
|
|
|
logrus.Debugf("reading local Dockerfile %q", dfile)
|
2018-02-24 18:09:25 +08:00
|
|
|
contents, err := os.Open(dfile)
|
|
|
|
if err != nil {
|
2018-10-12 04:58:04 +08:00
|
|
|
return "", nil, errors.Wrapf(err, "error reading %q", dfile)
|
2018-02-24 18:09:25 +08:00
|
|
|
}
|
2018-10-28 03:47:03 +08:00
|
|
|
dinfo, err = contents.Stat()
|
2018-02-24 18:09:25 +08:00
|
|
|
if err != nil {
|
|
|
|
contents.Close()
|
2018-10-12 04:58:04 +08:00
|
|
|
return "", nil, errors.Wrapf(err, "error reading info about %q", dfile)
|
2018-02-24 18:09:25 +08:00
|
|
|
}
|
2018-07-27 22:48:16 +08:00
|
|
|
if dinfo.Mode().IsRegular() && dinfo.Size() == 0 {
|
2018-02-24 18:09:25 +08:00
|
|
|
contents.Close()
|
2018-10-12 04:58:04 +08:00
|
|
|
return "", nil, errors.Wrapf(err, "no contents in %q", dfile)
|
2018-02-24 18:09:25 +08:00
|
|
|
}
|
2018-08-01 18:31:02 +08:00
|
|
|
data = contents
|
2017-03-28 15:06:13 +08:00
|
|
|
}
|
2018-08-01 18:31:02 +08:00
|
|
|
|
|
|
|
// pre-process Dockerfiles with ".in" suffix
|
|
|
|
if strings.HasSuffix(dfile, ".in") {
|
|
|
|
pData, err := preprocessDockerfileContents(data, options.ContextDirectory)
|
|
|
|
if err != nil {
|
2018-10-12 04:58:04 +08:00
|
|
|
return "", nil, err
|
2018-08-01 18:31:02 +08:00
|
|
|
}
|
|
|
|
data = *pData
|
|
|
|
}
|
|
|
|
|
|
|
|
dockerfiles = append(dockerfiles, data)
|
2017-03-28 15:06:13 +08:00
|
|
|
}
|
2018-11-17 06:43:48 +08:00
|
|
|
|
2018-02-25 06:40:44 +08:00
|
|
|
mainNode, err := imagebuilder.ParseDockerfile(dockerfiles[0])
|
|
|
|
if err != nil {
|
2018-10-12 04:58:04 +08:00
|
|
|
return "", nil, errors.Wrapf(err, "error parsing main Dockerfile")
|
2017-03-28 15:06:13 +08:00
|
|
|
}
|
2018-02-25 06:40:44 +08:00
|
|
|
for _, d := range dockerfiles[1:] {
|
|
|
|
additionalNode, err := imagebuilder.ParseDockerfile(d)
|
|
|
|
if err != nil {
|
2018-10-12 04:58:04 +08:00
|
|
|
return "", nil, errors.Wrapf(err, "error parsing additional Dockerfile")
|
2018-02-25 06:40:44 +08:00
|
|
|
}
|
|
|
|
mainNode.Children = append(mainNode.Children, additionalNode.Children...)
|
|
|
|
}
|
2019-04-16 23:13:31 +08:00
|
|
|
exec, err := NewExecutor(store, options, mainNode)
|
2018-02-25 06:40:44 +08:00
|
|
|
if err != nil {
|
2018-10-12 04:58:04 +08:00
|
|
|
return "", nil, errors.Wrapf(err, "error creating build executor")
|
2018-02-25 06:40:44 +08:00
|
|
|
}
|
|
|
|
b := imagebuilder.NewBuilder(options.Args)
|
2020-02-08 01:54:18 +08:00
|
|
|
defaultContainerConfig, err := config.Default()
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, errors.Wrapf(err, "failed to get container config")
|
|
|
|
}
|
|
|
|
b.Env = append(defaultContainerConfig.GetDefaultEnv(), b.Env...)
|
2018-11-08 18:31:14 +08:00
|
|
|
stages, err := imagebuilder.NewStages(mainNode, b)
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, errors.Wrap(err, "error reading multiple stages")
|
|
|
|
}
|
2019-02-03 07:31:44 +08:00
|
|
|
if options.Target != "" {
|
|
|
|
stagesTargeted, ok := stages.ThroughTarget(options.Target)
|
|
|
|
if !ok {
|
|
|
|
return "", nil, errors.Errorf("The target %q was not found in the provided Dockerfile", options.Target)
|
|
|
|
}
|
|
|
|
stages = stagesTargeted
|
|
|
|
}
|
2018-04-12 22:20:36 +08:00
|
|
|
return exec.Build(ctx, stages)
|
2017-03-28 15:06:13 +08:00
|
|
|
}
|
2018-08-01 00:02:06 +08:00
|
|
|
|
2018-08-01 18:31:02 +08:00
|
|
|
// 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.
|
2019-09-03 23:40:37 +08:00
|
|
|
func preprocessDockerfileContents(r io.Reader, ctxDir string) (rdrCloser *io.ReadCloser, err error) {
|
2018-08-01 18:31:02 +08:00
|
|
|
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{}
|
|
|
|
|
2019-12-24 19:31:09 +08:00
|
|
|
cmd := exec.Command(cppPath, "-E", "-iquote", ctxDir, "-traditional", "-undef", "-")
|
2018-08-01 18:31:02 +08:00
|
|
|
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 {
|
2020-04-04 04:34:43 +08:00
|
|
|
err = errors.Wrapf(err, "%v", strings.TrimSpace(stderr.String()))
|
2018-08-01 18:31:02 +08:00
|
|
|
}
|
|
|
|
return nil, errors.Wrapf(err, "error pre-processing Dockerfile")
|
|
|
|
}
|
|
|
|
|
|
|
|
rc := ioutil.NopCloser(bytes.NewReader(stdout.Bytes()))
|
|
|
|
return &rc, nil
|
|
|
|
}
|