mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #9707 from duglin/RenameDockerfile
Allow for Dockerfile to be named something else.
This commit is contained in:
commit
a67e7382b8
9 changed files with 253 additions and 32 deletions
|
@ -14,6 +14,7 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -84,6 +85,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
|
||||
forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers, even after unsuccessful builds")
|
||||
pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image")
|
||||
dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile(Default is 'Dockerfile' at context root)")
|
||||
|
||||
cmd.Require(flag.Exact, 1)
|
||||
|
||||
utils.ParseFlags(cmd, args, true)
|
||||
|
@ -109,7 +112,10 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err)
|
||||
}
|
||||
context, err = archive.Generate("Dockerfile", string(dockerfile))
|
||||
if *dockerfileName == "" {
|
||||
*dockerfileName = api.DefaultDockerfileName
|
||||
}
|
||||
context, err = archive.Generate(*dockerfileName, string(dockerfile))
|
||||
} else {
|
||||
context = ioutil.NopCloser(buf)
|
||||
}
|
||||
|
@ -136,9 +142,40 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
if _, err := os.Stat(root); err != nil {
|
||||
return err
|
||||
}
|
||||
filename := path.Join(root, "Dockerfile")
|
||||
|
||||
absRoot, err := filepath.Abs(root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var filename string // path to Dockerfile
|
||||
var origDockerfile string // used for error msg
|
||||
|
||||
if *dockerfileName == "" {
|
||||
// No -f/--file was specified so use the default
|
||||
origDockerfile = api.DefaultDockerfileName
|
||||
*dockerfileName = origDockerfile
|
||||
filename = path.Join(absRoot, *dockerfileName)
|
||||
} else {
|
||||
origDockerfile = *dockerfileName
|
||||
if filename, err = filepath.Abs(*dockerfileName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify that 'filename' is within the build context
|
||||
if !strings.HasSuffix(absRoot, string(os.PathSeparator)) {
|
||||
absRoot += string(os.PathSeparator)
|
||||
}
|
||||
if !strings.HasPrefix(filename, absRoot) {
|
||||
return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", *dockerfileName, root)
|
||||
}
|
||||
|
||||
// Now reset the dockerfileName to be relative to the build context
|
||||
*dockerfileName = filename[len(absRoot):]
|
||||
}
|
||||
|
||||
if _, err = os.Stat(filename); os.IsNotExist(err) {
|
||||
return fmt.Errorf("no Dockerfile found in %s", cmd.Arg(0))
|
||||
return fmt.Errorf("Can not locate Dockerfile: %s", origDockerfile)
|
||||
}
|
||||
var includes []string = []string{"."}
|
||||
|
||||
|
@ -147,16 +184,16 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// If .dockerignore mentions .dockerignore or Dockerfile
|
||||
// If .dockerignore mentions .dockerignore or the Dockerfile
|
||||
// then make sure we send both files over to the daemon
|
||||
// because Dockerfile is, obviously, needed no matter what, and
|
||||
// .dockerignore is needed to know if either one needs to be
|
||||
// removed. The deamon will remove them for us, if needed, after it
|
||||
// parses the Dockerfile.
|
||||
keepThem1, _ := fileutils.Matches(".dockerignore", excludes)
|
||||
keepThem2, _ := fileutils.Matches("Dockerfile", excludes)
|
||||
keepThem2, _ := fileutils.Matches(*dockerfileName, excludes)
|
||||
if keepThem1 || keepThem2 {
|
||||
includes = append(includes, ".dockerignore", "Dockerfile")
|
||||
includes = append(includes, ".dockerignore", *dockerfileName)
|
||||
}
|
||||
|
||||
if err = utils.ValidateContextDirectory(root, excludes); err != nil {
|
||||
|
@ -219,6 +256,9 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
if *pull {
|
||||
v.Set("pull", "1")
|
||||
}
|
||||
|
||||
v.Set("dockerfile", *dockerfileName)
|
||||
|
||||
cli.LoadConfigFile()
|
||||
|
||||
headers := http.Header(make(map[string][]string))
|
||||
|
|
|
@ -15,9 +15,10 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
APIVERSION version.Version = "1.16"
|
||||
DEFAULTHTTPHOST = "127.0.0.1"
|
||||
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
||||
APIVERSION version.Version = "1.16"
|
||||
DEFAULTHTTPHOST = "127.0.0.1"
|
||||
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
||||
DefaultDockerfileName string = "Dockerfile"
|
||||
)
|
||||
|
||||
func ValidateHost(val string) (string, error) {
|
||||
|
|
|
@ -1035,6 +1035,7 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
|
|||
}
|
||||
job.Stdin.Add(r.Body)
|
||||
job.Setenv("remote", r.FormValue("remote"))
|
||||
job.Setenv("dockerfile", r.FormValue("dockerfile"))
|
||||
job.Setenv("t", r.FormValue("t"))
|
||||
job.Setenv("q", r.FormValue("q"))
|
||||
job.Setenv("nocache", r.FormValue("nocache"))
|
||||
|
|
|
@ -105,13 +105,14 @@ type Builder struct {
|
|||
// both of these are controlled by the Remove and ForceRemove options in BuildOpts
|
||||
TmpContainers map[string]struct{} // a map of containers used for removes
|
||||
|
||||
dockerfile *parser.Node // the syntax tree of the dockerfile
|
||||
image string // image name for commit processing
|
||||
maintainer string // maintainer name. could probably be removed.
|
||||
cmdSet bool // indicates is CMD was set in current Dockerfile
|
||||
context tarsum.TarSum // the context is a tarball that is uploaded by the client
|
||||
contextPath string // the path of the temporary directory the local context is unpacked to (server side)
|
||||
noBaseImage bool // indicates that this build does not start from any base image, but is being built from an empty file system.
|
||||
dockerfileName string // name of Dockerfile
|
||||
dockerfile *parser.Node // the syntax tree of the dockerfile
|
||||
image string // image name for commit processing
|
||||
maintainer string // maintainer name. could probably be removed.
|
||||
cmdSet bool // indicates is CMD was set in current Dockerfile
|
||||
context tarsum.TarSum // the context is a tarball that is uploaded by the client
|
||||
contextPath string // the path of the temporary directory the local context is unpacked to (server side)
|
||||
noBaseImage bool // indicates that this build does not start from any base image, but is being built from an empty file system.
|
||||
}
|
||||
|
||||
// Run the builder with the context. This is the lynchpin of this package. This
|
||||
|
@ -137,7 +138,7 @@ func (b *Builder) Run(context io.Reader) (string, error) {
|
|||
}
|
||||
}()
|
||||
|
||||
if err := b.readDockerfile("Dockerfile"); err != nil {
|
||||
if err := b.readDockerfile(b.dockerfileName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
@ -205,9 +206,9 @@ func (b *Builder) readDockerfile(filename string) error {
|
|||
os.Remove(path.Join(b.contextPath, ".dockerignore"))
|
||||
b.context.(tarsum.BuilderContext).Remove(".dockerignore")
|
||||
}
|
||||
if rm, _ := fileutils.Matches("Dockerfile", excludes); rm == true {
|
||||
os.Remove(path.Join(b.contextPath, "Dockerfile"))
|
||||
b.context.(tarsum.BuilderContext).Remove("Dockerfile")
|
||||
if rm, _ := fileutils.Matches(b.dockerfileName, excludes); rm == true {
|
||||
os.Remove(path.Join(b.contextPath, b.dockerfileName))
|
||||
b.context.(tarsum.BuilderContext).Remove(b.dockerfileName)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/engine"
|
||||
"github.com/docker/docker/graph"
|
||||
|
@ -30,6 +31,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
|
|||
return job.Errorf("Usage: %s\n", job.Name)
|
||||
}
|
||||
var (
|
||||
dockerfileName = job.Getenv("dockerfile")
|
||||
remoteURL = job.Getenv("remote")
|
||||
repoName = job.Getenv("t")
|
||||
suppressOutput = job.GetenvBool("q")
|
||||
|
@ -42,6 +44,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
|
|||
tag string
|
||||
context io.ReadCloser
|
||||
)
|
||||
|
||||
job.GetenvJson("authConfig", authConfig)
|
||||
job.GetenvJson("configFile", configFile)
|
||||
|
||||
|
@ -57,6 +60,10 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
|
|||
}
|
||||
}
|
||||
|
||||
if dockerfileName == "" {
|
||||
dockerfileName = api.DefaultDockerfileName
|
||||
}
|
||||
|
||||
if remoteURL == "" {
|
||||
context = ioutil.NopCloser(job.Stdin)
|
||||
} else if urlutil.IsGitURL(remoteURL) {
|
||||
|
@ -88,7 +95,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
|
|||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
c, err := archive.Generate("Dockerfile", string(dockerFile))
|
||||
c, err := archive.Generate(dockerfileName, string(dockerFile))
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
@ -118,6 +125,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
|
|||
StreamFormatter: sf,
|
||||
AuthConfig: authConfig,
|
||||
AuthConfigFile: configFile,
|
||||
dockerfileName: dockerfileName,
|
||||
}
|
||||
|
||||
id, err := builder.Run(context)
|
||||
|
|
|
@ -7,6 +7,7 @@ docker-build - Build a new image from the source code at PATH
|
|||
# SYNOPSIS
|
||||
**docker build**
|
||||
[**--help**]
|
||||
[**-f**|**--file**[=*Dockerfile*]]
|
||||
[**--force-rm**[=*false*]]
|
||||
[**--no-cache**[=*false*]]
|
||||
[**-q**|**--quiet**[=*false*]]
|
||||
|
@ -31,6 +32,9 @@ When a Git repository is set as the **URL**, the repository is used
|
|||
as context.
|
||||
|
||||
# OPTIONS
|
||||
**-f**, **--file**=*Dockerfile*
|
||||
Path to the Dockerfile to use. If the path is a relative path then it must be relative to the current directory. The file must be within the build context. The default is *Dockerfile*.
|
||||
|
||||
**--force-rm**=*true*|*false*
|
||||
Always remove intermediate containers, even after unsuccessful builds. The default is *false*.
|
||||
|
||||
|
|
|
@ -1157,16 +1157,21 @@ Build an image from Dockerfile via stdin
|
|||
{"stream": "..."}
|
||||
{"error": "Error...", "errorDetail": {"code": 123, "message": "Error..."}}
|
||||
|
||||
The stream must be a tar archive compressed with one of the
|
||||
following algorithms: identity (no compression), gzip, bzip2, xz.
|
||||
The input stream must be a tar archive compressed with one of the
|
||||
following algorithms: identity (no compression), gzip, bzip2, xz.
|
||||
|
||||
The archive must include a file called `Dockerfile`
|
||||
at its root. It may include any number of other files,
|
||||
which will be accessible in the build context (See the [*ADD build
|
||||
command*](/reference/builder/#dockerbuilder)).
|
||||
The archive must include a build instructions file, typically called
|
||||
`Dockerfile` at the root of the archive. The `f` parameter may be used
|
||||
to specify a different build instructions file by having its value be
|
||||
the path to the alternate build instructions file to use.
|
||||
|
||||
The archive may include any number of other files,
|
||||
which will be accessible in the build context (See the [*ADD build
|
||||
command*](/reference/builder/#dockerbuilder)).
|
||||
|
||||
Query Parameters:
|
||||
|
||||
- **dockerfile** - path within the build context to the Dockerfile
|
||||
- **t** – repository name (and optionally a tag) to be applied to
|
||||
the resulting image in case of success
|
||||
- **q** – suppress verbose build output
|
||||
|
|
|
@ -461,11 +461,12 @@ To kill the container, use `docker kill`.
|
|||
|
||||
Build a new image from the source code at PATH
|
||||
|
||||
--force-rm=false Always remove intermediate containers, even after unsuccessful builds
|
||||
--no-cache=false Do not use cache when building the image
|
||||
-q, --quiet=false Suppress the verbose output generated by the containers
|
||||
--rm=true Remove intermediate containers after a successful build
|
||||
-t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success
|
||||
-f, --file="" Location of the Dockerfile to use. Default is 'Dockerfile' at the root of the build context
|
||||
--force-rm=false Always remove intermediate containers, even after unsuccessful builds
|
||||
--no-cache=false Do not use cache when building the image
|
||||
-q, --quiet=false Suppress the verbose output generated by the containers
|
||||
--rm=true Remove intermediate containers after a successful build
|
||||
-t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success
|
||||
|
||||
Use this command to build Docker images from a Dockerfile and a
|
||||
"context".
|
||||
|
@ -510,6 +511,13 @@ For example, the files `tempa`, `tempb` are ignored from the root directory.
|
|||
Currently there is no support for regular expressions. Formats
|
||||
like `[^temp*]` are ignored.
|
||||
|
||||
By default the `docker build` command will look for a `Dockerfile` at the
|
||||
root of the build context. The `-f`, `--file`, option lets you specify
|
||||
the path to an alternative file to use instead. This is useful
|
||||
in cases where the same set of files are used for multiple builds. The path
|
||||
must be to a file within the build context. If a relative path is specified
|
||||
then it must to be relative to the current directory.
|
||||
|
||||
|
||||
See also:
|
||||
|
||||
|
@ -612,6 +620,28 @@ repository is used as Dockerfile. Note that you
|
|||
can specify an arbitrary Git repository by using the `git://` or `git@`
|
||||
schema.
|
||||
|
||||
$ sudo docker build -f Dockerfile.debug .
|
||||
|
||||
This will use a file called `Dockerfile.debug` for the build
|
||||
instructions instead of `Dockerfile`.
|
||||
|
||||
$ sudo docker build -f dockerfiles/Dockerfile.debug -t myapp_debug .
|
||||
$ sudo docker build -f dockerfiles/Dockerfile.prod -t myapp_prod .
|
||||
|
||||
The above commands will build the current build context (as specified by
|
||||
the `.`) twice, once using a debug version of a `Dockerfile` and once using
|
||||
a production version.
|
||||
|
||||
$ cd /home/me/myapp/some/dir/really/deep
|
||||
$ sudo docker build -f /home/me/myapp/dockerfiles/debug /home/me/myapp
|
||||
$ sudo docker build -f ../../../../dockerfiles/debug /home/me/myapp
|
||||
|
||||
These two `docker build` commands do the exact same thing. They both
|
||||
use the contents of the `debug` file instead of looking for a `Dockerfile`
|
||||
and will use `/home/me/myapp` as the root of the build context. Note that
|
||||
`debug` is in the directory structure of the build context, regardless of how
|
||||
you refer to it on the command line.
|
||||
|
||||
> **Note:** `docker build` will return a `no such file or directory` error
|
||||
> if the file or directory does not exist in the uploaded context. This may
|
||||
> happen if there is no context, or if you specify a file that is elsewhere
|
||||
|
|
|
@ -3155,6 +3155,36 @@ func TestBuildDockerignoringDockerfile(t *testing.T) {
|
|||
logDone("build - test .dockerignore of Dockerfile")
|
||||
}
|
||||
|
||||
func TestBuildDockerignoringRenamedDockerfile(t *testing.T) {
|
||||
name := "testbuilddockerignoredockerfile"
|
||||
defer deleteImages(name)
|
||||
dockerfile := `
|
||||
FROM busybox
|
||||
ADD . /tmp/
|
||||
RUN ls /tmp/Dockerfile
|
||||
RUN ! ls /tmp/MyDockerfile
|
||||
RUN ls /tmp/.dockerignore`
|
||||
ctx, err := fakeContext(dockerfile, map[string]string{
|
||||
"Dockerfile": "Should not use me",
|
||||
"MyDockerfile": dockerfile,
|
||||
".dockerignore": "MyDockerfile\n",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = buildImageFromContext(name, ctx, true); err != nil {
|
||||
t.Fatalf("Didn't ignore MyDockerfile correctly:%s", err)
|
||||
}
|
||||
|
||||
// now try it with ./MyDockerfile
|
||||
ctx.Add(".dockerignore", "./MyDockerfile\n")
|
||||
if _, err = buildImageFromContext(name, ctx, true); err != nil {
|
||||
t.Fatalf("Didn't ignore ./MyDockerfile correctly:%s", err)
|
||||
}
|
||||
|
||||
logDone("build - test .dockerignore of renamed Dockerfile")
|
||||
}
|
||||
|
||||
func TestBuildDockerignoringDockerignore(t *testing.T) {
|
||||
name := "testbuilddockerignoredockerignore"
|
||||
defer deleteImages(name)
|
||||
|
@ -4170,3 +4200,104 @@ CMD cat /foo/file`,
|
|||
|
||||
logDone("build - volumes retain contents in build")
|
||||
}
|
||||
|
||||
func TestBuildRenamedDockerfile(t *testing.T) {
|
||||
defer deleteAllContainers()
|
||||
|
||||
ctx, err := fakeContext(`FROM busybox
|
||||
RUN echo from Dockerfile`,
|
||||
map[string]string{
|
||||
"Dockerfile": "FROM busybox\nRUN echo from Dockerfile",
|
||||
"files/Dockerfile": "FROM busybox\nRUN echo from files/Dockerfile",
|
||||
"files/dFile": "FROM busybox\nRUN echo from files/dFile",
|
||||
"dFile": "FROM busybox\nRUN echo from dFile",
|
||||
})
|
||||
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("Should have used Dockerfile, output:%s", out)
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdInDir(t, ctx.Dir, "build", "-f", "files/Dockerfile", "-t", "test2", ".")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(out, "from files/Dockerfile") {
|
||||
t.Fatalf("Should have used files/Dockerfile, output:%s", out)
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdInDir(t, ctx.Dir, "build", "--file=files/dFile", "-t", "test3", ".")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(out, "from files/dFile") {
|
||||
t.Fatalf("Should have used files/dFile, output:%s", out)
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdInDir(t, ctx.Dir, "build", "--file=dFile", "-t", "test4", ".")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(out, "from dFile") {
|
||||
t.Fatalf("Should have used dFile, output:%s", out)
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdInDir(t, ctx.Dir, "build", "--file=/etc/passwd", "-t", "test5", ".")
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("Was supposed to fail to find passwd")
|
||||
}
|
||||
|
||||
if !strings.Contains(out, "The Dockerfile (/etc/passwd) must be within the build context (.)") {
|
||||
t.Fatalf("Wrong error message for passwd:%v", out)
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdInDir(t, ctx.Dir+"/files", "build", "-f", "../Dockerfile", "-t", "test5", "..")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !strings.Contains(out, "from Dockerfile") {
|
||||
t.Fatalf("Should have used root Dockerfile, output:%s", out)
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdInDir(t, ctx.Dir+"/files", "build", "-f", ctx.Dir+"/files/Dockerfile", "-t", "test6", "..")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !strings.Contains(out, "from files/Dockerfile") {
|
||||
t.Fatalf("Should have used files Dockerfile - 2, output:%s", out)
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdInDir(t, ctx.Dir+"/files", "build", "-f", "../Dockerfile", "-t", "test7", ".")
|
||||
|
||||
if err == nil || !strings.Contains(out, "must be within the build context") {
|
||||
t.Fatalf("Should have failed with Dockerfile out of context")
|
||||
}
|
||||
|
||||
out, _, err = dockerCmdInDir(t, "/tmp", "build", "-t", "test6", ctx.Dir)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !strings.Contains(out, "from Dockerfile") {
|
||||
t.Fatalf("Should have used root Dockerfile, output:%s", out)
|
||||
}
|
||||
|
||||
logDone("build - rename dockerfile")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue