mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
commit
6e3fe93148
9 changed files with 177 additions and 30 deletions
2
.dockerignore
Normal file
2
.dockerignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
bundles
|
||||
.gopath
|
|
@ -6,3 +6,4 @@ Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
|||
AUTHORS: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
Dockerfile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
Makefile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
.dockerignore: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -163,7 +164,25 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
if err = utils.ValidateContextDirectory(root); err != nil {
|
||||
return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err)
|
||||
}
|
||||
context, err = archive.Tar(root, archive.Uncompressed)
|
||||
options := &archive.TarOptions{
|
||||
Compression: archive.Uncompressed,
|
||||
}
|
||||
if ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore")); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("Error reading .dockerignore: '%s'", err)
|
||||
} else if err == nil {
|
||||
for _, pattern := range strings.Split(string(ignore), "\n") {
|
||||
ok, err := filepath.Match(pattern, "Dockerfile")
|
||||
if err != nil {
|
||||
utils.Errorf("Bad .dockerignore pattern: '%s', error: %s", pattern, err)
|
||||
continue
|
||||
}
|
||||
if ok {
|
||||
return fmt.Errorf("Dockerfile was excluded by .dockerignore pattern '%s'", pattern)
|
||||
}
|
||||
options.Excludes = append(options.Excludes, pattern)
|
||||
}
|
||||
}
|
||||
context, err = archive.TarWithOptions(root, options)
|
||||
}
|
||||
var body io.Reader
|
||||
// Setup an upload progress bar
|
||||
|
|
|
@ -27,6 +27,7 @@ type (
|
|||
Compression int
|
||||
TarOptions struct {
|
||||
Includes []string
|
||||
Excludes []string
|
||||
Compression Compression
|
||||
NoLchown bool
|
||||
}
|
||||
|
@ -286,7 +287,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
|
|||
// Tar creates an archive from the directory at `path`, and returns it as a
|
||||
// stream of bytes.
|
||||
func Tar(path string, compression Compression) (io.ReadCloser, error) {
|
||||
return TarFilter(path, &TarOptions{Compression: compression})
|
||||
return TarWithOptions(path, &TarOptions{Compression: compression})
|
||||
}
|
||||
|
||||
func escapeName(name string) string {
|
||||
|
@ -305,12 +306,9 @@ func escapeName(name string) string {
|
|||
return string(escaped)
|
||||
}
|
||||
|
||||
// TarFilter creates an archive from the directory at `srcPath` with `options`, and returns it as a
|
||||
// stream of bytes.
|
||||
//
|
||||
// Files are included according to `options.Includes`, default to including all files.
|
||||
// Stream is compressed according to `options.Compression', default to Uncompressed.
|
||||
func TarFilter(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
||||
// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
|
||||
// paths are included in `options.Includes` (if non-nil) or not in `options.Excludes`.
|
||||
func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
|
||||
compressWriter, err := CompressStream(pipeWriter, options.Compression)
|
||||
|
@ -342,6 +340,21 @@ func TarFilter(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
for _, exclude := range options.Excludes {
|
||||
matched, err := filepath.Match(exclude, relFilePath)
|
||||
if err != nil {
|
||||
utils.Errorf("Error matching: %s (pattern: %s)", relFilePath, exclude)
|
||||
return err
|
||||
}
|
||||
if matched {
|
||||
utils.Debugf("Skipping excluded path: %s", relFilePath)
|
||||
if f.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := addTarFile(filePath, relFilePath, tw); err != nil {
|
||||
utils.Debugf("Can't add file %s to tar: %s\n", srcPath, err)
|
||||
}
|
||||
|
@ -482,7 +495,7 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
|||
// TarUntar aborts and returns the error.
|
||||
func TarUntar(src string, dst string) error {
|
||||
utils.Debugf("TarUntar(%s %s)", src, dst)
|
||||
archive, err := TarFilter(src, &TarOptions{Compression: Uncompressed})
|
||||
archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -63,8 +63,8 @@ func TestCmdStreamGood(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func tarUntar(t *testing.T, origin string, compression Compression) error {
|
||||
archive, err := Tar(origin, compression)
|
||||
func tarUntar(t *testing.T, origin string, options *TarOptions) ([]Change, error) {
|
||||
archive, err := TarWithOptions(origin, options)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -72,37 +72,29 @@ func tarUntar(t *testing.T, origin string, compression Compression) error {
|
|||
|
||||
buf := make([]byte, 10)
|
||||
if _, err := archive.Read(buf); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
wrap := io.MultiReader(bytes.NewReader(buf), archive)
|
||||
|
||||
detectedCompression := DetectCompression(buf)
|
||||
compression := options.Compression
|
||||
if detectedCompression.Extension() != compression.Extension() {
|
||||
return fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
|
||||
return nil, fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
|
||||
}
|
||||
|
||||
tmp, err := ioutil.TempDir("", "docker-test-untar")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
if err := Untar(wrap, tmp, nil); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if _, err := os.Stat(tmp); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
changes, err := ChangesDirs(origin, tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(changes) != 0 {
|
||||
t.Fatalf("Unexpected differences after tarUntar: %v", changes)
|
||||
}
|
||||
|
||||
return nil
|
||||
return ChangesDirs(origin, tmp)
|
||||
}
|
||||
|
||||
func TestTarUntar(t *testing.T) {
|
||||
|
@ -122,9 +114,49 @@ func TestTarUntar(t *testing.T) {
|
|||
Uncompressed,
|
||||
Gzip,
|
||||
} {
|
||||
if err := tarUntar(t, origin, c); err != nil {
|
||||
changes, err := tarUntar(t, origin, &TarOptions{
|
||||
Compression: c,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
|
||||
}
|
||||
|
||||
if len(changes) != 0 {
|
||||
t.Fatalf("Unexpected differences after tarUntar: %v", changes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTarWithOptions(t *testing.T) {
|
||||
origin, err := ioutil.TempDir("", "docker-test-untar-origin")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(origin)
|
||||
if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
opts *TarOptions
|
||||
numChanges int
|
||||
}{
|
||||
{&TarOptions{Includes: []string{"1"}}, 1},
|
||||
{&TarOptions{Excludes: []string{"2"}}, 1},
|
||||
}
|
||||
for _, testCase := range cases {
|
||||
changes, err := tarUntar(t, origin, testCase.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Error tar/untar when testing inclusion/exclusion: %s", err)
|
||||
}
|
||||
if len(changes) != testCase.numChanges {
|
||||
t.Errorf("Expected %d changes, got %d for %+v:",
|
||||
testCase.numChanges, len(changes), testCase.opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +177,7 @@ func TestTarUntarFile(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tar, err := TarFilter(path.Join(origin, "before"), &TarOptions{Compression: Uncompressed, Includes: []string{"file"}})
|
||||
tar, err := TarWithOptions(path.Join(origin, "before"), &TarOptions{Compression: Uncompressed, Includes: []string{"file"}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -813,7 +813,7 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
|||
basePath = path.Dir(basePath)
|
||||
}
|
||||
|
||||
archive, err := archive.TarFilter(basePath, &archive.TarOptions{
|
||||
archive, err := archive.TarWithOptions(basePath, &archive.TarOptions{
|
||||
Compression: archive.Uncompressed,
|
||||
Includes: filter,
|
||||
})
|
||||
|
|
|
@ -295,7 +295,7 @@ func (a *Driver) Put(id string) {
|
|||
|
||||
// Returns an archive of the contents for the id
|
||||
func (a *Driver) Diff(id string) (archive.Archive, error) {
|
||||
return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
||||
return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
|
||||
Compression: archive.Uncompressed,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -215,6 +215,12 @@ temporary directory on your local host, and then this is sent to the
|
|||
Docker daemon as the context. This way, your local user credentials and
|
||||
vpn's etc can be used to access private repositories.
|
||||
|
||||
If a file named ``.dockerignore`` exists in the root of ``PATH`` then it is
|
||||
interpreted as a newline-separated list of exclusion patterns. Exclusion
|
||||
patterns match files or directories relative to ``PATH`` that will be excluded
|
||||
from the context. Globbing is done using Go's
|
||||
[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules.
|
||||
|
||||
See also:
|
||||
|
||||
[*Dockerfile Reference*](/reference/builder/#dockerbuilder).
|
||||
|
@ -266,6 +272,30 @@ If you wish to keep the intermediate containers after the build is
|
|||
complete, you must use `--rm=false`. This does not
|
||||
affect the build cache.
|
||||
|
||||
$ docker build .
|
||||
Uploading context 18.829 MB
|
||||
Uploading context
|
||||
Step 0 : FROM busybox
|
||||
---> 769b9341d937
|
||||
Step 1 : CMD echo Hello World
|
||||
---> Using cache
|
||||
---> 99cc1ad10469
|
||||
Successfully built 99cc1ad10469
|
||||
$ echo ".git" > .dockerignore
|
||||
$ docker build .
|
||||
Uploading context 6.76 MB
|
||||
Uploading context
|
||||
Step 0 : FROM busybox
|
||||
---> 769b9341d937
|
||||
Step 1 : CMD echo Hello World
|
||||
---> Using cache
|
||||
---> 99cc1ad10469
|
||||
Successfully built 99cc1ad10469
|
||||
|
||||
This example shows the use of the ``.dockerignore`` file to exclude the ``.git``
|
||||
directory the context. Its effect can be seen in the changed size of the
|
||||
uploaded context.
|
||||
|
||||
$ sudo docker build -t vieux/apache:2.0 .
|
||||
|
||||
This will build like the previous example, but it will then tag the
|
||||
|
|
|
@ -1514,3 +1514,53 @@ docker.com>"
|
|||
|
||||
logDone("build - validate escaping whitespace")
|
||||
}
|
||||
|
||||
func TestDockerignore(t *testing.T) {
|
||||
name := "testbuilddockerignore"
|
||||
defer deleteImages(name)
|
||||
dockerfile := `
|
||||
FROM busybox
|
||||
ADD . /bla
|
||||
RUN [[ -f /bla/src/x.go ]]
|
||||
RUN [[ -f /bla/Makefile ]]
|
||||
RUN [[ ! -e /bla/src/_vendor ]]
|
||||
RUN [[ ! -e /bla/.gitignore ]]
|
||||
RUN [[ ! -e /bla/README.md ]]
|
||||
RUN [[ ! -e /bla/.git ]]`
|
||||
ctx, err := fakeContext(dockerfile, map[string]string{
|
||||
"Makefile": "all:",
|
||||
".git/HEAD": "ref: foo",
|
||||
"src/x.go": "package main",
|
||||
"src/_vendor/v.go": "package main",
|
||||
".gitignore": "",
|
||||
"README.md": "readme",
|
||||
".dockerignore": ".git\npkg\n.gitignore\nsrc/_vendor\n*.md",
|
||||
})
|
||||
defer ctx.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := buildImageFromContext(name, ctx, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
logDone("build - test .dockerignore")
|
||||
}
|
||||
|
||||
func TestDockerignoringDockerfile(t *testing.T) {
|
||||
name := "testbuilddockerignoredockerfile"
|
||||
defer deleteImages(name)
|
||||
dockerfile := `
|
||||
FROM scratch`
|
||||
ctx, err := fakeContext(dockerfile, map[string]string{
|
||||
"Dockerfile": "FROM scratch",
|
||||
".dockerignore": "Dockerfile\n",
|
||||
})
|
||||
defer ctx.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = buildImageFromContext(name, ctx, true); err == nil {
|
||||
t.Fatalf("Didn't get expected error from ignoring Dockerfile")
|
||||
}
|
||||
logDone("build - test .dockerignore of Dockerfile")
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue