func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {

	patterns, patDirs, exceptions, err := fileutils.CleanPatterns(options.ExcludePatterns)

	if err != nil {
		return nil, err
	}

	pipeReader, pipeWriter := io.Pipe()

	go func() {
		ta := &tarAppender{
			TarWriter: tar.NewWriter(pipeWriter),
			Buffer:    pools.BufioWriter32KPool.Get(nil),
			SeenFiles: make(map[uint64]string),
		}

		defer func() {
			// Make sure to check the error on Close.
			if err := ta.TarWriter.Close(); err != nil {
				log.Debugf("Can't close tar writer: %s", err)
			}
			if err := pipeWriter.Close(); err != nil {
				log.Debugf("Can't close pipe writer: %s", err)
			}
		}()

		// this buffer is needed for the duration of this piped stream
		defer pools.BufioWriter32KPool.Put(ta.Buffer)

		// In general we log errors here but ignore them because
		// during e.g. a diff operation the container can continue
		// mutating the filesystem and we can see transient errors
		// from this

		stat, err := os.Lstat(srcPath)
		if err != nil {
			return
		}

		if !stat.IsDir() {
			// We can't later join a non-dir with any includes because the
			// 'walk' will error if "file/." is stat-ed and "file" is not a
			// directory. So, we must split the source path and use the
			// basename as the include.
			if len(options.IncludeFiles) > 0 {
				log.Warn("Tar: Can't archive a file with includes")
			}

			dir, base := SplitPathDirEntry(srcPath)
			srcPath = dir
			options.IncludeFiles = []string{base}
		}

		if len(options.IncludeFiles) == 0 {
			options.IncludeFiles = []string{"."}
		}

		seen := make(map[string]bool)

		var renamedRelFilePath string // For when tar.Options.Name is set
		for _, include := range options.IncludeFiles {
			// We can't use filepath.Join(srcPath, include) because this will
			// clean away a trailing "." or "/" which may be important.
			walkRoot := strings.Join([]string{srcPath, include}, string(filepath.Separator))
			filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error {
				if err != nil {
					log.Debugf("Tar: Can't stat file %s to tar: %s", srcPath, err)
					return nil
				}

				relFilePath, err := filepath.Rel(srcPath, filePath)
				if err != nil || (!options.IncludeSourceDir && relFilePath == "." && f.IsDir()) {
					// Error getting relative path OR we are looking
					// at the source directory path. Skip in both situations.
					return nil
				}

				if options.IncludeSourceDir && include == "." && relFilePath != "." {
					relFilePath = strings.Join([]string{".", relFilePath}, string(filepath.Separator))
				}

				skip := false

				// If "include" is an exact match for the current file
				// then even if there's an "excludePatterns" pattern that
				// matches it, don't skip it. IOW, assume an explicit 'include'
				// is asking for that file no matter what - which is true
				// for some files, like .dockerignore and Dockerfile (sometimes)
				if include != relFilePath {
					skip, err = fileutils.OptimizedMatches(relFilePath, patterns, patDirs)
					if err != nil {
						log.Debugf("Error matching %s", relFilePath, err)
						return err
					}
				}

				if skip {
					if !exceptions && f.IsDir() {
						return filepath.SkipDir
					}
					return nil
				}

				if seen[relFilePath] {
					return nil
				}
				seen[relFilePath] = true

				// Rename the base resource
				if options.Name != "" && filePath == srcPath+"/"+filepath.Base(relFilePath) {
					renamedRelFilePath = relFilePath
				}
				// Set this to make sure the items underneath also get renamed
				if options.Name != "" {
					relFilePath = strings.Replace(relFilePath, renamedRelFilePath, options.Name, 1)
				}

				if err := ta.addTarFile(filePath, relFilePath); err != nil {
					log.Debugf("Can't add file %s to tar: %s", filePath, err)
				}
				return nil
			})
		}
	}()

	return pipeReader, nil
}
// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
// paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
TarWithOptions creates an archive from the directory at `path`, only including files whose relative
paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
// TarWithOptions creates an archive from the directory at ` path ` only including files whose relative // paths are included in ` options . IncludeFiles ` ( if non - nil ) or not in ` options . ExcludePatterns ` .
func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
	patterns, patDirs, exceptions, err := fileutils.CleanPatterns(options.ExcludePatterns)
	if err != nil {
		return nil, err
	}
	pipeReader, pipeWriter := io.Pipe()
	go func() {
		ta := &tarAppender{
			TarWriter: tar.NewWriter(pipeWriter),
			Buffer:    pools.BufioWriter32KPool.Get(nil),
			SeenFiles: make(map[uint64]string),
		}
		defer func() {
			if err := ta.TarWriter.Close(); err != nil {
				log.Debugf("Can't close tar writer: %s", err)
			}
			if err := pipeWriter.Close(); err != nil {
				log.Debugf("Can't close pipe writer: %s", err)
			}
		}()
		defer pools.BufioWriter32KPool.Put(ta.Buffer)
		stat, err := os.Lstat(srcPath)
		if err != nil {
			return
		}
		if !stat.IsDir() {
			if len(options.IncludeFiles) > 0 {
				log.Warn("Tar: Can't archive a file with includes")
			}
			dir, base := SplitPathDirEntry(srcPath)
			srcPath = dir
			options.IncludeFiles = []string{base}
		}
		if len(options.IncludeFiles) == 0 {
			options.IncludeFiles = []string{"."}
		}
		seen := make(map[string]bool)
		var renamedRelFilePath string 
		for _, include := range options.IncludeFiles {
			walkRoot := strings.Join([]string{srcPath, include}, string(filepath.Separator))
			filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error {
				if err != nil {
					log.Debugf("Tar: Can't stat file %s to tar: %s", srcPath, err)
					return nil
				}
				relFilePath, err := filepath.Rel(srcPath, filePath)
				if err != nil || (!options.IncludeSourceDir && relFilePath == "." && f.IsDir()) {
					return nil
				}
				if options.IncludeSourceDir && include == "." && relFilePath != "." {
					relFilePath = strings.Join([]string{".", relFilePath}, string(filepath.Separator))
				}
				skip := false
				if include != relFilePath {
					skip, err = fileutils.OptimizedMatches(relFilePath, patterns, patDirs)
					if err != nil {
						log.Debugf("Error matching %s", relFilePath, err)
						return err
					}
				}
				if skip {
					if !exceptions && f.IsDir() {
						return filepath.SkipDir
					}
					return nil
				}
				if seen[relFilePath] {
					return nil
				}
				seen[relFilePath] = true
				if options.Name != "" && filePath == srcPath+"/"+filepath.Base(relFilePath) {
					renamedRelFilePath = relFilePath
				}
				if options.Name != "" {
					relFilePath = strings.Replace(relFilePath, renamedRelFilePath, options.Name, 1)
				}
				if err := ta.addTarFile(filePath, relFilePath); err != nil {
					log.Debugf("Can't add file %s to tar: %s", filePath, err)
				}
				return nil
			})
		}
	}()
	return pipeReader, nil
}
func TarWithOptions ( srcPath string , options * TarOptions ) ( io . ReadCloser , error ) { patterns , patDirs , exceptions , err := fileutils . CleanPatterns ( options . ExcludePatterns ) if err != nil { return nil , err } pipeReader , pipeWriter := io . Pipe ( ) go func ( ) { ta := & tarAppender { TarWriter : tar . NewWriter ( pipeWriter ) , Buffer : pools . BufioWriter32KPool . Get ( nil ) , SeenFiles : make ( map [ uint64 ] string ) , } defer func ( ) { if err := ta . TarWriter . Close ( ) ; err != nil { log . Debugf ( " " , err ) } if err := pipeWriter . Close ( ) ; err != nil { log . Debugf ( " " , err ) } } ( ) defer pools . BufioWriter32KPool . Put ( ta . Buffer ) stat , err := os . Lstat ( srcPath ) if err != nil { return } if ! stat . IsDir ( ) { if len ( options . IncludeFiles ) > 0 { log . Warn ( " " ) } dir , base := SplitPathDirEntry ( srcPath ) srcPath = dir options . IncludeFiles = [ ] string { base } } if len ( options . IncludeFiles ) == 0 { options . IncludeFiles = [ ] string { " " } } seen := make ( map [ string ] bool ) var renamedRelFilePath string for _ , include := range options . IncludeFiles { walkRoot := strings . Join ( [ ] string { srcPath , include } , string ( filepath . Separator ) ) filepath . Walk ( walkRoot , func ( filePath string , f os . FileInfo , err error ) error { if err != nil { log . Debugf ( " " , srcPath , err ) return nil } relFilePath , err := filepath . Rel ( srcPath , filePath ) if err != nil || ( ! options . IncludeSourceDir && relFilePath == " " && f . IsDir ( ) ) { return nil } if options . IncludeSourceDir && include == " " && relFilePath != " " { relFilePath = strings . Join ( [ ] string { " " , relFilePath } , string ( filepath . Separator ) ) } skip := false if include != relFilePath { skip , err = fileutils . OptimizedMatches ( relFilePath , patterns , patDirs ) if err != nil { log . Debugf ( " " , relFilePath , err ) return err } } if skip { if ! exceptions && f . IsDir ( ) { return filepath . SkipDir } return nil } if seen [ relFilePath ] { return nil } seen [ relFilePath ] = true if options . Name != " " && filePath == srcPath + " " + filepath . Base ( relFilePath ) { renamedRelFilePath = relFilePath } if options . Name != " " { relFilePath = strings . Replace ( relFilePath , renamedRelFilePath , options . Name , 1 ) } if err := ta . addTarFile ( filePath , relFilePath ) ; err != nil { log . Debugf ( " " , filePath , err ) } return nil } ) } } ( ) return pipeReader , nil }
TarWithOptions creates an archive from the directory at `path`, only including files whose relative
paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
TarWithOptions creates an archive from the directory at ` path ` only including files whose relative paths are included in ` options . IncludeFiles ` ( if non - nil ) or not in ` options . ExcludePatterns ` .
// Make sure to check the error on Close. // this buffer is needed for the duration of this piped stream // In general we log errors here but ignore them because // during e.g. a diff operation the container can continue // mutating the filesystem and we can see transient errors // from this // We can't later join a non-dir with any includes because the // 'walk' will error if "file/." is stat-ed and "file" is not a // directory. So, we must split the source path and use the // basename as the include. // For when tar.Options.Name is set // We can't use filepath.Join(srcPath, include) because this will // clean away a trailing "." or "/" which may be important. // Error getting relative path OR we are looking // at the source directory path. Skip in both situations. // If "include" is an exact match for the current file // then even if there's an "excludePatterns" pattern that // matches it, don't skip it. IOW, assume an explicit 'include' // is asking for that file no matter what - which is true // for some files, like .dockerignore and Dockerfile (sometimes) // Rename the base resource // Set this to make sure the items underneath also get renamed