From 3ae91aff9ee67acac64f3e97908e55c5912a33fe Mon Sep 17 00:00:00 2001 From: Kazım SARIKAYA Date: Sun, 3 Jan 2021 19:04:25 +0300 Subject: podman-remote fix sending tar content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.) podman cannot send proper dockerfile when it is not inside root folder. 2.) support for sending symlinks and folders inside context dir 3.) when sending context dir as tar to remote, prevent sending items inside .dockerignore Signed-off-by: Kazım SARIKAYA --- pkg/bindings/images/build.go | 138 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 115 insertions(+), 23 deletions(-) (limited to 'pkg') diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index d34ab87d9..02765816f 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -2,6 +2,7 @@ package images import ( "archive/tar" + "compress/gzip" "context" "encoding/json" "io" @@ -18,6 +19,7 @@ import ( "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/storage/pkg/fileutils" "github.com/docker/go-units" "github.com/hashicorp/go-multierror" jsoniter "github.com/json-iterator/go" @@ -138,12 +140,38 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO entries := make([]string, len(containerFiles)) copy(entries, containerFiles) entries = append(entries, options.ContextDirectory) - tarfile, err := nTar(entries...) + + excludes := options.Excludes + if len(excludes) == 0 { + excludes, err = parseDockerignore(options.ContextDirectory) + if err != nil { + return nil, err + } + } + + tarfile, err := nTar(excludes, entries...) if err != nil { + logrus.Errorf("cannot tar container entries %v error: %v", entries, err) return nil, err } defer tarfile.Close() - params.Set("dockerfile", filepath.Base(containerFiles[0])) + + containerFile, err := filepath.Abs(entries[0]) + if err != nil { + logrus.Errorf("cannot find absolute path of %v: %v", entries[0], err) + return nil, err + } + contextDir, err := filepath.Abs(entries[1]) + if err != nil { + logrus.Errorf("cannot find absolute path of %v: %v", entries[1], err) + return nil, err + } + + if strings.HasPrefix(containerFile, contextDir+string(filepath.Separator)) { + containerFile = strings.TrimPrefix(containerFile, contextDir+string(filepath.Separator)) + } + + params.Set("dockerfile", containerFile) conn, err := bindings.GetClient(ctx) if err != nil { @@ -200,52 +228,116 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } } -func nTar(sources ...string) (io.ReadCloser, error) { +func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { + pm, err := fileutils.NewPatternMatcher(excludes) + if err != nil { + return nil, errors.Wrapf(err, "error processing excludes list %v", excludes) + } + if len(sources) == 0 { return nil, errors.New("No source(s) provided for build") } pr, pw := io.Pipe() - tw := tar.NewWriter(pw) + gw := gzip.NewWriter(pw) + tw := tar.NewWriter(gw) var merr error go func() { defer pw.Close() + defer gw.Close() defer tw.Close() for _, src := range sources { - s := src - err := filepath.Walk(s, func(path string, info os.FileInfo, err error) error { + s, err := filepath.Abs(src) + if err != nil { + logrus.Errorf("cannot stat one of source context: %v", err) + merr = multierror.Append(merr, err) + return + } + + err = filepath.Walk(s, func(path string, info os.FileInfo, err error) error { if err != nil { return err } - if !info.Mode().IsRegular() || path == s { - return nil - } - f, lerr := os.Open(path) - if lerr != nil { - return lerr + if path == s { + return nil // skip root dir } name := strings.TrimPrefix(path, s+string(filepath.Separator)) - hdr, lerr := tar.FileInfoHeader(info, name) - if lerr != nil { - f.Close() - return lerr + + excluded, err := pm.Matches(filepath.ToSlash(name)) // nolint:staticcheck + if err != nil { + return errors.Wrapf(err, "error checking if %q is excluded", name) } - hdr.Name = name - if lerr := tw.WriteHeader(hdr); lerr != nil { - f.Close() - return lerr + if excluded { + return nil } - _, cerr := io.Copy(tw, f) - f.Close() - return cerr + if info.Mode().IsRegular() { // add file item + f, lerr := os.Open(path) + if lerr != nil { + return lerr + } + + hdr, lerr := tar.FileInfoHeader(info, name) + if lerr != nil { + f.Close() + return lerr + } + hdr.Name = name + if lerr := tw.WriteHeader(hdr); lerr != nil { + f.Close() + return lerr + } + + _, cerr := io.Copy(tw, f) + f.Close() + return cerr + } else if info.Mode().IsDir() { // add folders + hdr, lerr := tar.FileInfoHeader(info, name) + if lerr != nil { + return lerr + } + hdr.Name = name + if lerr := tw.WriteHeader(hdr); lerr != nil { + return lerr + } + } else if info.Mode()&os.ModeSymlink != 0 { // add symlinks as it, not content + link, err := os.Readlink(path) + if err != nil { + return err + } + hdr, lerr := tar.FileInfoHeader(info, link) + if lerr != nil { + return lerr + } + hdr.Name = name + if lerr := tw.WriteHeader(hdr); lerr != nil { + return lerr + } + } //skip other than file,folder and symlinks + return nil }) merr = multierror.Append(merr, err) } }() return pr, merr } + +func parseDockerignore(root string) ([]string, error) { + ignore, err := ioutil.ReadFile(filepath.Join(root, ".dockerignore")) + if err != nil && !os.IsNotExist(err) { + return nil, errors.Wrapf(err, "error reading .dockerignore: '%s'", root) + } + rawexcludes := strings.Split(string(ignore), "\n") + excludes := make([]string, 0, len(rawexcludes)) + for _, e := range rawexcludes { + if len(e) == 0 || e[0] == '#' { + continue + } + excludes = append(excludes, e) + } + return excludes, nil +} -- cgit v1.2.3-54-g00ecf