mirror of https://github.com/docker/cli.git
Merge pull request #31236 from tonistiigi/docker-stdin
build: accept -f - to read Dockerfile from stdin
This commit is contained in:
commit
eddc5b9d9e
|
@ -6,10 +6,12 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/docker/api"
|
"github.com/docker/docker/api"
|
||||||
|
@ -25,6 +27,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
"github.com/docker/docker/pkg/progress"
|
"github.com/docker/docker/pkg/progress"
|
||||||
"github.com/docker/docker/pkg/streamformatter"
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/docker/pkg/urlutil"
|
"github.com/docker/docker/pkg/urlutil"
|
||||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
|
@ -141,6 +144,7 @@ func (out *lastProgressOutput) WriteProgress(prog progress.Progress) error {
|
||||||
func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
|
func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
|
||||||
var (
|
var (
|
||||||
buildCtx io.ReadCloser
|
buildCtx io.ReadCloser
|
||||||
|
dockerfileCtx io.ReadCloser
|
||||||
err error
|
err error
|
||||||
contextDir string
|
contextDir string
|
||||||
tempDir string
|
tempDir string
|
||||||
|
@ -157,6 +161,13 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
|
||||||
buildBuff = bytes.NewBuffer(nil)
|
buildBuff = bytes.NewBuffer(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options.dockerfileName == "-" {
|
||||||
|
if specifiedContext == "-" {
|
||||||
|
return errors.New("invalid argument: can't use stdin for both build context and dockerfile")
|
||||||
|
}
|
||||||
|
dockerfileCtx = dockerCli.In()
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case specifiedContext == "-":
|
case specifiedContext == "-":
|
||||||
buildCtx, relDockerfile, err = build.GetContextFromReader(dockerCli.In(), options.dockerfileName)
|
buildCtx, relDockerfile, err = build.GetContextFromReader(dockerCli.In(), options.dockerfileName)
|
||||||
|
@ -207,18 +218,19 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
|
||||||
return errors.Errorf("Error checking context: '%s'.", err)
|
return errors.Errorf("Error checking context: '%s'.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If .dockerignore mentions .dockerignore or the Dockerfile
|
// If .dockerignore mentions .dockerignore or the Dockerfile then make
|
||||||
// then make sure we send both files over to the daemon
|
// sure we send both files over to the daemon because Dockerfile is,
|
||||||
// because Dockerfile is, obviously, needed no matter what, and
|
// obviously, needed no matter what, and .dockerignore is needed to know
|
||||||
// .dockerignore is needed to know if either one needs to be
|
// if either one needs to be removed. The daemon will remove them
|
||||||
// removed. The daemon will remove them for us, if needed, after it
|
// if necessary, after it parses the Dockerfile. Ignore errors here, as
|
||||||
// parses the Dockerfile. Ignore errors here, as they will have been
|
// they will have been caught by validateContextDirectory above.
|
||||||
// caught by validateContextDirectory above.
|
// Excludes are used instead of includes to maintain the order of files
|
||||||
var includes = []string{"."}
|
// in the archive.
|
||||||
keepThem1, _ := fileutils.Matches(".dockerignore", excludes)
|
if keep, _ := fileutils.Matches(".dockerignore", excludes); keep {
|
||||||
keepThem2, _ := fileutils.Matches(relDockerfile, excludes)
|
excludes = append(excludes, "!.dockerignore")
|
||||||
if keepThem1 || keepThem2 {
|
}
|
||||||
includes = append(includes, ".dockerignore", relDockerfile)
|
if keep, _ := fileutils.Matches(relDockerfile, excludes); keep && dockerfileCtx == nil {
|
||||||
|
excludes = append(excludes, "!"+relDockerfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
compression := archive.Uncompressed
|
compression := archive.Uncompressed
|
||||||
|
@ -228,13 +240,20 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
|
||||||
buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
|
buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
|
||||||
Compression: compression,
|
Compression: compression,
|
||||||
ExcludePatterns: excludes,
|
ExcludePatterns: excludes,
|
||||||
IncludeFiles: includes,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replace Dockerfile if added dynamically
|
||||||
|
if dockerfileCtx != nil {
|
||||||
|
buildCtx, relDockerfile, err = addDockerfileToBuildContext(dockerfileCtx, buildCtx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
var resolvedTags []*resolvedTag
|
var resolvedTags []*resolvedTag
|
||||||
|
@ -338,6 +357,50 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addDockerfileToBuildContext(dockerfileCtx io.ReadCloser, buildCtx io.ReadCloser) (io.ReadCloser, string, error) {
|
||||||
|
file, err := ioutil.ReadAll(dockerfileCtx)
|
||||||
|
dockerfileCtx.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
hdrTmpl := &tar.Header{
|
||||||
|
Mode: 0600,
|
||||||
|
Uid: 0,
|
||||||
|
Gid: 0,
|
||||||
|
ModTime: now,
|
||||||
|
Typeflag: tar.TypeReg,
|
||||||
|
AccessTime: now,
|
||||||
|
ChangeTime: now,
|
||||||
|
}
|
||||||
|
randomName := ".dockerfile." + stringid.GenerateRandomID()[:20]
|
||||||
|
|
||||||
|
buildCtx = archive.ReplaceFileTarWrapper(buildCtx, map[string]archive.TarModifierFunc{
|
||||||
|
// Add the dockerfile with a random filename
|
||||||
|
randomName: func(_ string, h *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
|
||||||
|
return hdrTmpl, file, nil
|
||||||
|
},
|
||||||
|
// Update .dockerignore to include the random filename
|
||||||
|
".dockerignore": func(_ string, h *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
|
||||||
|
if h == nil {
|
||||||
|
h = hdrTmpl
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
if content != nil {
|
||||||
|
if _, err := b.ReadFrom(content); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b.WriteString(".dockerignore")
|
||||||
|
}
|
||||||
|
b.WriteString("\n" + randomName + "\n")
|
||||||
|
return h, b.Bytes(), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return buildCtx, randomName, nil
|
||||||
|
}
|
||||||
|
|
||||||
func isLocalDir(c string) bool {
|
func isLocalDir(c string) bool {
|
||||||
_, err := os.Stat(c)
|
_, err := os.Stat(c)
|
||||||
return err == nil
|
return err == nil
|
||||||
|
|
|
@ -89,6 +89,10 @@ func GetContextFromReader(r io.ReadCloser, dockerfileName string) (out io.ReadCl
|
||||||
return ioutils.NewReadCloserWrapper(buf, func() error { return r.Close() }), dockerfileName, nil
|
return ioutils.NewReadCloserWrapper(buf, func() error { return r.Close() }), dockerfileName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dockerfileName == "-" {
|
||||||
|
return nil, "", errors.New("build context is not an archive")
|
||||||
|
}
|
||||||
|
|
||||||
// Input should be read as a Dockerfile.
|
// Input should be read as a Dockerfile.
|
||||||
tmpDir, err := ioutil.TempDir("", "docker-build-context-")
|
tmpDir, err := ioutil.TempDir("", "docker-build-context-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -166,7 +170,7 @@ func GetContextFromLocalDir(localDir, dockerfileName string) (absContextDir, rel
|
||||||
// When using a local context directory, when the Dockerfile is specified
|
// When using a local context directory, when the Dockerfile is specified
|
||||||
// with the `-f/--file` option then it is considered relative to the
|
// with the `-f/--file` option then it is considered relative to the
|
||||||
// current directory and not the context directory.
|
// current directory and not the context directory.
|
||||||
if dockerfileName != "" {
|
if dockerfileName != "" && dockerfileName != "-" {
|
||||||
if dockerfileName, err = filepath.Abs(dockerfileName); err != nil {
|
if dockerfileName, err = filepath.Abs(dockerfileName); err != nil {
|
||||||
return "", "", errors.Errorf("unable to get absolute path to Dockerfile: %v", err)
|
return "", "", errors.Errorf("unable to get absolute path to Dockerfile: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -220,6 +224,8 @@ func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDi
|
||||||
absDockerfile = altPath
|
absDockerfile = altPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if absDockerfile == "-" {
|
||||||
|
absDockerfile = filepath.Join(absContextDir, DefaultDockerfileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not already an absolute path, the Dockerfile path should be joined to
|
// If not already an absolute path, the Dockerfile path should be joined to
|
||||||
|
@ -234,18 +240,21 @@ func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDi
|
||||||
// an issue in golang. On Windows, EvalSymLinks does not work on UNC file
|
// an issue in golang. On Windows, EvalSymLinks does not work on UNC file
|
||||||
// paths (those starting with \\). This hack means that when using links
|
// paths (those starting with \\). This hack means that when using links
|
||||||
// on UNC paths, they will not be followed.
|
// on UNC paths, they will not be followed.
|
||||||
if !isUNC(absDockerfile) {
|
if givenDockerfile != "-" {
|
||||||
absDockerfile, err = filepath.EvalSymlinks(absDockerfile)
|
if !isUNC(absDockerfile) {
|
||||||
if err != nil {
|
absDockerfile, err = filepath.EvalSymlinks(absDockerfile)
|
||||||
return "", "", errors.Errorf("unable to evaluate symlinks in Dockerfile path: %v", err)
|
if err != nil {
|
||||||
}
|
return "", "", errors.Errorf("unable to evaluate symlinks in Dockerfile path: %v", err)
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Lstat(absDockerfile); err != nil {
|
}
|
||||||
if os.IsNotExist(err) {
|
}
|
||||||
return "", "", errors.Errorf("Cannot locate Dockerfile: %q", absDockerfile)
|
|
||||||
|
if _, err := os.Lstat(absDockerfile); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", "", errors.Errorf("Cannot locate Dockerfile: %q", absDockerfile)
|
||||||
|
}
|
||||||
|
return "", "", errors.Errorf("unable to stat Dockerfile: %v", err)
|
||||||
}
|
}
|
||||||
return "", "", errors.Errorf("unable to stat Dockerfile: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if relDockerfile, err = filepath.Rel(absContextDir, absDockerfile); err != nil {
|
if relDockerfile, err = filepath.Rel(absContextDir, absDockerfile); err != nil {
|
||||||
|
|
Loading…
Reference in New Issue