Skip to content

Commit

Permalink
Merge pull request #886 from thaJeztah/dockerfile-outside-context
Browse files Browse the repository at this point in the history
Allow Dockerfile from outside build-context
  • Loading branch information
Vincent Demeester authored Feb 20, 2018
2 parents 02e8dc6 + a104852 commit 82a8085
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 6 deletions.
14 changes: 12 additions & 2 deletions cli/command/image/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import (
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"

"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
Expand Down Expand Up @@ -206,6 +208,14 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
buildCtx, relDockerfile, err = build.GetContextFromReader(dockerCli.In(), options.dockerfileName)
case isLocalDir(specifiedContext):
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):
tempDir, relDockerfile, err = build.GetContextFromGitURL(specifiedContext, options.dockerfileName)
case urlutil.IsURL(specifiedContext):
Expand Down Expand Up @@ -253,15 +263,15 @@ 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 {
buildCtx, relDockerfile, err = build.AddDockerfileToBuildContext(dockerfileCtx, buildCtx)
if err != nil {
return err
}
}

// 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
if options.stream && dockerfileCtx == nil {
dockerfileCtx, err = os.Open(relDockerfile)
Expand Down
8 changes: 4 additions & 4 deletions cli/command/image/build/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ func GetContextFromGitURL(gitURL, dockerfileName string) (string, string, error)
return "", "", err
}
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
}

Expand Down Expand Up @@ -318,10 +322,6 @@ func getDockerfileRelPath(absContextDir, givenDockerfile string) (string, error)
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
}

Expand Down
50 changes: 50 additions & 0 deletions cli/command/image/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,56 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
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
// starting with `github.com/` are special-cased, and the build command attempts
// to clone the remote repo.
Expand Down

0 comments on commit 82a8085

Please sign in to comment.