mirror of https://github.com/docker/cli.git
Allow Dockerfile from outside build-context
Historically, the Dockerfile had to be insde the build-context, because it was
sent as part of the build-context.
3f6dc81e10
added support for passing the Dockerfile through stdin, in which case the
contents of the Dockerfile is injected into the build-context.
This patch uses the same mechanism for situations where the location of the
Dockerfile is passed, and its path is outside of the build-context.
Before this change:
$ mkdir -p myproject/context myproject/dockerfiles && cd myproject
$ echo "hello" > context/hello
$ echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > dockerfiles/Dockerfile
$ docker build --no-cache -f $PWD/dockerfiles/Dockerfile $PWD/context
unable to prepare context: the Dockerfile (/Users/sebastiaan/projects/test/dockerfile-outside/myproject/dockerfiles/Dockerfile) must be within the build context
After this change:
$ mkdir -p myproject/context myproject/dockerfiles && cd myproject
$ echo "hello" > context/hello
$ echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > dockerfiles/Dockerfile
$ docker build --no-cache -f $PWD/dockerfiles/Dockerfile $PWD/context
Sending build context to Docker daemon 2.607kB
Step 1/3 : FROM busybox
---> 6ad733544a63
Step 2/3 : COPY /hello /
---> 9a5ae1c7be9e
Step 3/3 : RUN cat /hello
---> Running in 20dfef2d180f
hello
Removing intermediate container 20dfef2d180f
---> ce1748f91bb2
Successfully built ce1748f91bb2
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
a0044ba3a7
commit
a1048523d2
|
@ -9,8 +9,10 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
@ -206,6 +208,14 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||||
buildCtx, relDockerfile, err = build.GetContextFromReader(dockerCli.In(), options.dockerfileName)
|
buildCtx, relDockerfile, err = build.GetContextFromReader(dockerCli.In(), options.dockerfileName)
|
||||||
case isLocalDir(specifiedContext):
|
case isLocalDir(specifiedContext):
|
||||||
contextDir, relDockerfile, err = build.GetContextFromLocalDir(specifiedContext, options.dockerfileName)
|
contextDir, relDockerfile, err = build.GetContextFromLocalDir(specifiedContext, options.dockerfileName)
|
||||||
|
if err == nil && strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
||||||
|
// Dockerfile is outside of build-context; read the Dockerfile and pass it as dockerfileCtx
|
||||||
|
dockerfileCtx, err = os.Open(options.dockerfileName)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("unable to open Dockerfile: %v", err)
|
||||||
|
}
|
||||||
|
defer dockerfileCtx.Close()
|
||||||
|
}
|
||||||
case urlutil.IsGitURL(specifiedContext):
|
case urlutil.IsGitURL(specifiedContext):
|
||||||
tempDir, relDockerfile, err = build.GetContextFromGitURL(specifiedContext, options.dockerfileName)
|
tempDir, relDockerfile, err = build.GetContextFromGitURL(specifiedContext, options.dockerfileName)
|
||||||
case urlutil.IsURL(specifiedContext):
|
case urlutil.IsURL(specifiedContext):
|
||||||
|
@ -253,7 +263,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace Dockerfile if it was added from stdin and there is archive context
|
// replace Dockerfile if it was added from stdin or a file outside the build-context, and there is archive context
|
||||||
if dockerfileCtx != nil && buildCtx != nil {
|
if dockerfileCtx != nil && buildCtx != nil {
|
||||||
buildCtx, relDockerfile, err = build.AddDockerfileToBuildContext(dockerfileCtx, buildCtx)
|
buildCtx, relDockerfile, err = build.AddDockerfileToBuildContext(dockerfileCtx, buildCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -261,7 +271,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if streaming and dockerfile was not from stdin then read from file
|
// if streaming and Dockerfile was not from stdin then read from file
|
||||||
// to the same reader that is usually stdin
|
// to the same reader that is usually stdin
|
||||||
if options.stream && dockerfileCtx == nil {
|
if options.stream && dockerfileCtx == nil {
|
||||||
dockerfileCtx, err = os.Open(relDockerfile)
|
dockerfileCtx, err = os.Open(relDockerfile)
|
||||||
|
|
|
@ -167,6 +167,10 @@ func GetContextFromGitURL(gitURL, dockerfileName string) (string, string, error)
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
relDockerfile, err := getDockerfileRelPath(absContextDir, dockerfileName)
|
relDockerfile, err := getDockerfileRelPath(absContextDir, dockerfileName)
|
||||||
|
if err == nil && strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
||||||
|
return "", "", errors.Errorf("the Dockerfile (%s) must be within the build context", dockerfileName)
|
||||||
|
}
|
||||||
|
|
||||||
return absContextDir, relDockerfile, err
|
return absContextDir, relDockerfile, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,10 +322,6 @@ func getDockerfileRelPath(absContextDir, givenDockerfile string) (string, error)
|
||||||
return "", errors.Errorf("unable to get relative Dockerfile path: %v", err)
|
return "", errors.Errorf("unable to get relative Dockerfile path: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
|
||||||
return "", errors.Errorf("the Dockerfile (%s) must be within the build context", givenDockerfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
return relDockerfile, nil
|
return relDockerfile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,56 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
|
||||||
assert.Equal(t, []string{dockerfileName, ".dockerignore", "foo"}, actual)
|
assert.Equal(t, []string{dockerfileName, ".dockerignore", "foo"}, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunBuildDockerfileOutsideContext(t *testing.T) {
|
||||||
|
dir := fs.NewDir(t, t.Name(),
|
||||||
|
fs.WithFile("data", "data file"),
|
||||||
|
)
|
||||||
|
defer dir.Remove()
|
||||||
|
|
||||||
|
// Dockerfile outside of build-context
|
||||||
|
df := fs.NewFile(t, t.Name(),
|
||||||
|
fs.WithContent(`
|
||||||
|
FROM FOOBAR
|
||||||
|
COPY data /data
|
||||||
|
`),
|
||||||
|
)
|
||||||
|
defer df.Remove()
|
||||||
|
|
||||||
|
dest, err := ioutil.TempDir("", t.Name())
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(dest)
|
||||||
|
|
||||||
|
var dockerfileName string
|
||||||
|
fakeImageBuild := func(_ context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
tee := io.TeeReader(context, buffer)
|
||||||
|
|
||||||
|
assert.NoError(t, archive.Untar(tee, dest, nil))
|
||||||
|
dockerfileName = options.Dockerfile
|
||||||
|
|
||||||
|
body := new(bytes.Buffer)
|
||||||
|
return types.ImageBuildResponse{Body: ioutil.NopCloser(body)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeImageBuild})
|
||||||
|
|
||||||
|
options := newBuildOptions()
|
||||||
|
options.context = dir.Path()
|
||||||
|
options.dockerfileName = df.Path()
|
||||||
|
|
||||||
|
err = runBuild(cli, options)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(dest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
var actual []string
|
||||||
|
for _, fileInfo := range files {
|
||||||
|
actual = append(actual, fileInfo.Name())
|
||||||
|
}
|
||||||
|
sort.Strings(actual)
|
||||||
|
assert.Equal(t, []string{dockerfileName, ".dockerignore", "data"}, actual)
|
||||||
|
}
|
||||||
|
|
||||||
// TestRunBuildFromLocalGitHubDirNonExistingRepo tests that build contexts
|
// TestRunBuildFromLocalGitHubDirNonExistingRepo tests that build contexts
|
||||||
// starting with `github.com/` are special-cased, and the build command attempts
|
// starting with `github.com/` are special-cased, and the build command attempts
|
||||||
// to clone the remote repo.
|
// to clone the remote repo.
|
||||||
|
|
Loading…
Reference in New Issue