123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- // Copyright 2015 go-dockerclient authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package docker
- import (
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path"
- "path/filepath"
- "strings"
- "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive"
- "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/fileutils"
- )
- func createTarStream(srcPath, dockerfilePath string) (io.ReadCloser, error) {
- excludes, err := parseDockerignore(srcPath)
- if err != nil {
- return nil, err
- }
- includes := []string{"."}
- // 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.
- //
- // https://github.com/docker/docker/issues/8330
- //
- forceIncludeFiles := []string{".dockerignore", dockerfilePath}
- for _, includeFile := range forceIncludeFiles {
- if includeFile == "" {
- continue
- }
- keepThem, err := fileutils.Matches(includeFile, excludes)
- if err != nil {
- return nil, fmt.Errorf("cannot match .dockerfile: '%s', error: %s", includeFile, err)
- }
- if keepThem {
- includes = append(includes, includeFile)
- }
- }
- if err := validateContextDirectory(srcPath, excludes); err != nil {
- return nil, err
- }
- tarOpts := &archive.TarOptions{
- ExcludePatterns: excludes,
- IncludeFiles: includes,
- Compression: archive.Uncompressed,
- NoLchown: true,
- }
- return archive.TarWithOptions(srcPath, tarOpts)
- }
- // validateContextDirectory checks if all the contents of the directory
- // can be read and returns an error if some files can't be read.
- // Symlinks which point to non-existing files don't trigger an error
- func validateContextDirectory(srcPath string, excludes []string) error {
- return filepath.Walk(filepath.Join(srcPath, "."), func(filePath string, f os.FileInfo, err error) error {
- // skip this directory/file if it's not in the path, it won't get added to the context
- if relFilePath, err := filepath.Rel(srcPath, filePath); err != nil {
- return err
- } else if skip, err := fileutils.Matches(relFilePath, excludes); err != nil {
- return err
- } else if skip {
- if f.IsDir() {
- return filepath.SkipDir
- }
- return nil
- }
- if err != nil {
- if os.IsPermission(err) {
- return fmt.Errorf("can't stat '%s'", filePath)
- }
- if os.IsNotExist(err) {
- return nil
- }
- return err
- }
- // skip checking if symlinks point to non-existing files, such symlinks can be useful
- // also skip named pipes, because they hanging on open
- if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 {
- return nil
- }
- if !f.IsDir() {
- currentFile, err := os.Open(filePath)
- if err != nil && os.IsPermission(err) {
- return fmt.Errorf("no permission to read from '%s'", filePath)
- }
- currentFile.Close()
- }
- return nil
- })
- }
- func parseDockerignore(root string) ([]string, error) {
- var excludes []string
- ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore"))
- if err != nil && !os.IsNotExist(err) {
- return excludes, fmt.Errorf("error reading .dockerignore: '%s'", err)
- }
- excludes = strings.Split(string(ignore), "\n")
- return excludes, nil
- }
|