mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Support dockerfile and Dockerfile
Closes #10807 Adds support for `dockerfile` ONLY when `Dockerfile` can't be found. If we're building from a Dockerfile via stdin/URL then always download it a `Dockerfile` and ignore the -f flag. Signed-off-by: Doug Davis <dug@us.ibm.com>
This commit is contained in:
parent
a61716e5d9
commit
15924f2385
7 changed files with 282 additions and 25 deletions
|
@ -114,9 +114,10 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err)
|
return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err)
|
||||||
}
|
}
|
||||||
if *dockerfileName == "" {
|
|
||||||
|
// -f option has no meaning when we're reading it from stdin,
|
||||||
|
// so just use our default Dockerfile name
|
||||||
*dockerfileName = api.DefaultDockerfileName
|
*dockerfileName = api.DefaultDockerfileName
|
||||||
}
|
|
||||||
context, err = archive.Generate(*dockerfileName, string(dockerfile))
|
context, err = archive.Generate(*dockerfileName, string(dockerfile))
|
||||||
} else {
|
} else {
|
||||||
context = ioutil.NopCloser(buf)
|
context = ioutil.NopCloser(buf)
|
||||||
|
@ -156,6 +157,16 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||||
// No -f/--file was specified so use the default
|
// No -f/--file was specified so use the default
|
||||||
*dockerfileName = api.DefaultDockerfileName
|
*dockerfileName = api.DefaultDockerfileName
|
||||||
filename = filepath.Join(absRoot, *dockerfileName)
|
filename = filepath.Join(absRoot, *dockerfileName)
|
||||||
|
|
||||||
|
// Just to be nice ;-) look for 'dockerfile' too but only
|
||||||
|
// use it if we found it, otherwise ignore this check
|
||||||
|
if _, err = os.Lstat(filename); os.IsNotExist(err) {
|
||||||
|
tmpFN := path.Join(absRoot, strings.ToLower(*dockerfileName))
|
||||||
|
if _, err = os.Lstat(tmpFN); err == nil {
|
||||||
|
*dockerfileName = strings.ToLower(*dockerfileName)
|
||||||
|
filename = tmpFN
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
origDockerfile := *dockerfileName // used for error msg
|
origDockerfile := *dockerfileName // used for error msg
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/api"
|
||||||
"github.com/docker/docker/builder/command"
|
"github.com/docker/docker/builder/command"
|
||||||
"github.com/docker/docker/builder/parser"
|
"github.com/docker/docker/builder/parser"
|
||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
|
@ -146,7 +147,7 @@ func (b *Builder) Run(context io.Reader) (string, error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := b.readDockerfile(b.dockerfileName); err != nil {
|
if err := b.readDockerfile(); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +178,23 @@ func (b *Builder) Run(context io.Reader) (string, error) {
|
||||||
|
|
||||||
// Reads a Dockerfile from the current context. It assumes that the
|
// Reads a Dockerfile from the current context. It assumes that the
|
||||||
// 'filename' is a relative path from the root of the context
|
// 'filename' is a relative path from the root of the context
|
||||||
func (b *Builder) readDockerfile(origFile string) error {
|
func (b *Builder) readDockerfile() error {
|
||||||
|
// If no -f was specified then look for 'Dockerfile'. If we can't find
|
||||||
|
// that then look for 'dockerfile'. If neither are found then default
|
||||||
|
// back to 'Dockerfile' and use that in the error message.
|
||||||
|
if b.dockerfileName == "" {
|
||||||
|
b.dockerfileName = api.DefaultDockerfileName
|
||||||
|
tmpFN := filepath.Join(b.contextPath, api.DefaultDockerfileName)
|
||||||
|
if _, err := os.Lstat(tmpFN); err != nil {
|
||||||
|
tmpFN = filepath.Join(b.contextPath, strings.ToLower(api.DefaultDockerfileName))
|
||||||
|
if _, err := os.Lstat(tmpFN); err == nil {
|
||||||
|
b.dockerfileName = strings.ToLower(api.DefaultDockerfileName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
origFile := b.dockerfileName
|
||||||
|
|
||||||
filename, err := symlink.FollowSymlinkInScope(filepath.Join(b.contextPath, origFile), b.contextPath)
|
filename, err := symlink.FollowSymlinkInScope(filepath.Join(b.contextPath, origFile), b.contextPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("The Dockerfile (%s) must be within the build context", origFile)
|
return fmt.Errorf("The Dockerfile (%s) must be within the build context", origFile)
|
||||||
|
|
|
@ -78,10 +78,6 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dockerfileName == "" {
|
|
||||||
dockerfileName = api.DefaultDockerfileName
|
|
||||||
}
|
|
||||||
|
|
||||||
if remoteURL == "" {
|
if remoteURL == "" {
|
||||||
context = ioutil.NopCloser(job.Stdin)
|
context = ioutil.NopCloser(job.Stdin)
|
||||||
} else if urlutil.IsGitURL(remoteURL) {
|
} else if urlutil.IsGitURL(remoteURL) {
|
||||||
|
@ -113,6 +109,11 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When we're downloading just a Dockerfile put it in
|
||||||
|
// the default name - don't allow the client to move/specify it
|
||||||
|
dockerfileName = api.DefaultDockerfileName
|
||||||
|
|
||||||
c, err := archive.Generate(dockerfileName, string(dockerFile))
|
c, err := archive.Generate(dockerfileName, string(dockerFile))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
|
|
|
@ -1073,10 +1073,13 @@ command*](/reference/builder/#dockerbuilder)).
|
||||||
|
|
||||||
Query Parameters:
|
Query Parameters:
|
||||||
|
|
||||||
- **dockerfile** - path within the build context to the Dockerfile
|
- **dockerfile** - path within the build context to the Dockerfile. This is
|
||||||
|
ignored if `remote` is specified and points to an individual filename.
|
||||||
- **t** – repository name (and optionally a tag) to be applied to
|
- **t** – repository name (and optionally a tag) to be applied to
|
||||||
the resulting image in case of success
|
the resulting image in case of success
|
||||||
- **remote** – git or HTTP/HTTPS URI build source
|
- **remote** – A Git repository URI or HTTP/HTTPS URI build source. If the
|
||||||
|
URI specifies a filename, the file's contents are placed into a file
|
||||||
|
called `Dockerfile`.
|
||||||
- **q** – suppress verbose build output
|
- **q** – suppress verbose build output
|
||||||
- **nocache** – do not use the cache when building the image
|
- **nocache** – do not use the cache when building the image
|
||||||
- **pull** - attempt to pull the image even if an older image exists locally
|
- **pull** - attempt to pull the image even if an older image exists locally
|
||||||
|
|
|
@ -506,21 +506,27 @@ is returned by the `docker attach` command to its caller too:
|
||||||
--rm=true Remove intermediate containers after a successful build
|
--rm=true Remove intermediate containers after a successful build
|
||||||
-t, --tag="" Repository name (and optionally a tag) for the image
|
-t, --tag="" Repository name (and optionally a tag) for the image
|
||||||
|
|
||||||
Use this command to build Docker images from a Dockerfile and a
|
Builds Docker images from a Dockerfile and a "context". A build's context is
|
||||||
"context".
|
the files located in the specified `PATH` or `URL`. The build process can
|
||||||
|
refer to any of the files in the context. For example, your build can use
|
||||||
|
an [*ADD*](/reference/builder/#add) instruction to reference a file in the
|
||||||
|
context.
|
||||||
|
|
||||||
The files at `PATH` or `URL` are called the "context" of the build. The
|
The `URL` parameter can specify the location of a Git repository; in this
|
||||||
build process may refer to any of the files in the context, for example
|
case, the repository is the context. The Git repository is recursively
|
||||||
when using an [*ADD*](/reference/builder/#add) instruction.
|
cloned with its submodules. The system does a fresh `git clone -recursive`
|
||||||
When a single Dockerfile is given as `URL` or is piped through `STDIN`
|
in a temporary directory on your local host. Then, this clone is sent to
|
||||||
(`docker build - < Dockerfile`), then no context is set.
|
the Docker daemon as the context. Local clones give you the ability to
|
||||||
|
access private repositories using local user credentials, VPN's, and so forth.
|
||||||
|
|
||||||
When a Git repository is set as `URL`, then the repository is used as
|
Instead of specifying a context, you can pass a single Dockerfile in the
|
||||||
the context. The Git repository is cloned with its submodules
|
`URL` or pipe the file in via `STDIN`. To pipe a Dockerfile from `STDIN`:
|
||||||
(`git clone -recursive`). A fresh `git clone` occurs in a temporary directory
|
|
||||||
on your local host, and then this is sent to the Docker daemon as the
|
docker build - < Dockerfile
|
||||||
context. This way, your local user credentials and VPN's etc can be
|
|
||||||
used to access private repositories.
|
If you use STDIN or specify a `URL`, the system places the contents into a
|
||||||
|
file called `Dockerfile`, and any `-f`, `--file` option is ignored. In this
|
||||||
|
scenario, there is no context.
|
||||||
|
|
||||||
If a file named `.dockerignore` exists in the root of `PATH` then it
|
If a file named `.dockerignore` exists in the root of `PATH` then it
|
||||||
is interpreted as a newline-separated list of exclusion patterns.
|
is interpreted as a newline-separated list of exclusion patterns.
|
||||||
|
@ -529,7 +535,7 @@ will be excluded from the context. Globbing is done using Go's
|
||||||
[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules.
|
[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules.
|
||||||
|
|
||||||
Please note that `.dockerignore` files in other subdirectories are
|
Please note that `.dockerignore` files in other subdirectories are
|
||||||
considered as normal files. Filepaths in .dockerignore are absolute with
|
considered as normal files. Filepaths in `.dockerignore` are absolute with
|
||||||
the current directory as the root. Wildcards are allowed but the search
|
the current directory as the root. Wildcards are allowed but the search
|
||||||
is not recursive.
|
is not recursive.
|
||||||
|
|
||||||
|
|
|
@ -353,6 +353,106 @@ func TestBuildApiDockerfilePath(t *testing.T) {
|
||||||
logDone("container REST API - check build w/bad Dockerfile path")
|
logDone("container REST API - check build w/bad Dockerfile path")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildApiDockerFileRemote(t *testing.T) {
|
||||||
|
server, err := fakeStorage(map[string]string{
|
||||||
|
"testD": `FROM busybox
|
||||||
|
COPY * /tmp/
|
||||||
|
RUN find /tmp/`,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
buf, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL+"/testD", nil, "application/json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Build failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := string(buf)
|
||||||
|
if !strings.Contains(out, "/tmp/Dockerfile") ||
|
||||||
|
strings.Contains(out, "/tmp/baz") {
|
||||||
|
t.Fatalf("Incorrect output: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("container REST API - check build with -f from remote")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildApiLowerDockerfile(t *testing.T) {
|
||||||
|
git, err := fakeGIT("repo", map[string]string{
|
||||||
|
"dockerfile": `FROM busybox
|
||||||
|
RUN echo from dockerfile`,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer git.Close()
|
||||||
|
|
||||||
|
buf, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Build failed: %s\n%q", err, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := string(buf)
|
||||||
|
if !strings.Contains(out, "from dockerfile") {
|
||||||
|
t.Fatalf("Incorrect output: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("container REST API - check build with lower dockerfile")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildApiBuildGitWithF(t *testing.T) {
|
||||||
|
git, err := fakeGIT("repo", map[string]string{
|
||||||
|
"baz": `FROM busybox
|
||||||
|
RUN echo from baz`,
|
||||||
|
"Dockerfile": `FROM busybox
|
||||||
|
RUN echo from Dockerfile`,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer git.Close()
|
||||||
|
|
||||||
|
// Make sure it tries to 'dockerfile' query param value
|
||||||
|
buf, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+git.RepoURL, nil, "application/json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Build failed: %s\n%q", err, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := string(buf)
|
||||||
|
if !strings.Contains(out, "from baz") {
|
||||||
|
t.Fatalf("Incorrect output: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("container REST API - check build from git w/F")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildApiDoubleDockerfile(t *testing.T) {
|
||||||
|
git, err := fakeGIT("repo", map[string]string{
|
||||||
|
"Dockerfile": `FROM busybox
|
||||||
|
RUN echo from Dockerfile`,
|
||||||
|
"dockerfile": `FROM busybox
|
||||||
|
RUN echo from dockerfile`,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer git.Close()
|
||||||
|
|
||||||
|
// Make sure it tries to 'dockerfile' query param value
|
||||||
|
buf, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Build failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := string(buf)
|
||||||
|
if !strings.Contains(out, "from Dockerfile") {
|
||||||
|
t.Fatalf("Incorrect output: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("container REST API - check build with two dockerfiles")
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildApiDockerfileSymlink(t *testing.T) {
|
func TestBuildApiDockerfileSymlink(t *testing.T) {
|
||||||
// Test to make sure we stop people from trying to leave the
|
// Test to make sure we stop people from trying to leave the
|
||||||
// build context when specifying a symlink as the path to the dockerfile
|
// build context when specifying a symlink as the path to the dockerfile
|
||||||
|
|
|
@ -4699,6 +4699,125 @@ func TestBuildRenamedDockerfile(t *testing.T) {
|
||||||
logDone("build - rename dockerfile")
|
logDone("build - rename dockerfile")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildFromMixedcaseDockerfile(t *testing.T) {
|
||||||
|
defer deleteImages("test1")
|
||||||
|
|
||||||
|
ctx, err := fakeContext(`FROM busybox
|
||||||
|
RUN echo from dockerfile`,
|
||||||
|
map[string]string{
|
||||||
|
"dockerfile": "FROM busybox\nRUN echo from dockerfile",
|
||||||
|
})
|
||||||
|
defer ctx.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, _, err := dockerCmdInDir(t, ctx.Dir, "build", "-t", "test1", ".")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to build: %s\n%s", out, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(out, "from dockerfile") {
|
||||||
|
t.Fatalf("Missing proper output: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("build - mixedcase Dockerfile")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildWithTwoDockerfiles(t *testing.T) {
|
||||||
|
defer deleteImages("test1")
|
||||||
|
|
||||||
|
ctx, err := fakeContext(`FROM busybox
|
||||||
|
RUN echo from Dockerfile`,
|
||||||
|
map[string]string{
|
||||||
|
"dockerfile": "FROM busybox\nRUN echo from dockerfile",
|
||||||
|
})
|
||||||
|
defer ctx.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, _, err := dockerCmdInDir(t, ctx.Dir, "build", "-t", "test1", ".")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to build: %s\n%s", out, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(out, "from Dockerfile") {
|
||||||
|
t.Fatalf("Missing proper output: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("build - two Dockerfiles")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildFromURLWithF(t *testing.T) {
|
||||||
|
defer deleteImages("test1")
|
||||||
|
|
||||||
|
server, err := fakeStorage(map[string]string{"baz": `FROM busybox
|
||||||
|
RUN echo from baz
|
||||||
|
COPY * /tmp/
|
||||||
|
RUN find /tmp/`})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
ctx, err := fakeContext(`FROM busybox
|
||||||
|
RUN echo from Dockerfile`,
|
||||||
|
map[string]string{})
|
||||||
|
defer ctx.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that -f is ignored and that we don't use the Dockerfile
|
||||||
|
// that's in the current dir
|
||||||
|
out, _, err := dockerCmdInDir(t, ctx.Dir, "build", "-f", "baz", "-t", "test1", server.URL+"/baz")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to build: %s\n%s", out, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(out, "from baz") ||
|
||||||
|
strings.Contains(out, "/tmp/baz") ||
|
||||||
|
!strings.Contains(out, "/tmp/Dockerfile") {
|
||||||
|
t.Fatalf("Missing proper output: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("build - from URL with -f")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildFromStdinWithF(t *testing.T) {
|
||||||
|
defer deleteImages("test1")
|
||||||
|
|
||||||
|
ctx, err := fakeContext(`FROM busybox
|
||||||
|
RUN echo from Dockerfile`,
|
||||||
|
map[string]string{})
|
||||||
|
defer ctx.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that -f is ignored and that we don't use the Dockerfile
|
||||||
|
// that's in the current dir
|
||||||
|
dockerCommand := exec.Command(dockerBinary, "build", "-f", "baz", "-t", "test1", "-")
|
||||||
|
dockerCommand.Dir = ctx.Dir
|
||||||
|
dockerCommand.Stdin = strings.NewReader(`FROM busybox
|
||||||
|
RUN echo from baz
|
||||||
|
COPY * /tmp/
|
||||||
|
RUN find /tmp/`)
|
||||||
|
out, status, err := runCommandWithOutput(dockerCommand)
|
||||||
|
if err != nil || status != 0 {
|
||||||
|
t.Fatalf("Error building: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(out, "from baz") ||
|
||||||
|
strings.Contains(out, "/tmp/baz") ||
|
||||||
|
!strings.Contains(out, "/tmp/Dockerfile") {
|
||||||
|
t.Fatalf("Missing proper output: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
logDone("build - from stdin with -f")
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildFromOfficialNames(t *testing.T) {
|
func TestBuildFromOfficialNames(t *testing.T) {
|
||||||
name := "testbuildfromofficial"
|
name := "testbuildfromofficial"
|
||||||
fromNames := []string{
|
fromNames := []string{
|
||||||
|
|
Loading…
Reference in a new issue