mirror of https://github.com/docker/docs.git
Make TarFilter more useful
There are a few changes: * Callers can specify if they want recursive behaviour or not * All file listings to tar are sent on stdin, to handle long lists better * We can pass in a list of filenames which will be created as empty files in the tarball This is exactly what we want for the creation of layer tarballs given a container fs, a set of files to add and a set of whiteout files to create.
This commit is contained in:
parent
8f23945f7f
commit
223280f319
82
archive.go
82
archive.go
|
|
@ -80,21 +80,73 @@ func (compression *Compression) Extension() string {
|
||||||
// Tar creates an archive from the directory at `path`, and returns it as a
|
// Tar creates an archive from the directory at `path`, and returns it as a
|
||||||
// stream of bytes.
|
// stream of bytes.
|
||||||
func Tar(path string, compression Compression) (io.Reader, error) {
|
func Tar(path string, compression Compression) (io.Reader, error) {
|
||||||
return TarFilter(path, compression, nil)
|
return TarFilter(path, compression, nil, true, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeName(name string) string {
|
||||||
|
escaped := make([]byte,0)
|
||||||
|
for i, c := range []byte(name) {
|
||||||
|
if i == 0 && c == '/' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// all printable chars except "-" which is 0x2d
|
||||||
|
if (0x20 <= c && c <= 0x7E) && c != 0x2d {
|
||||||
|
escaped = append(escaped, c)
|
||||||
|
} else {
|
||||||
|
escaped = append(escaped, fmt.Sprintf("\\%03o", c)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(escaped)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tar creates an archive from the directory at `path`, only including files whose relative
|
// Tar creates an archive from the directory at `path`, only including files whose relative
|
||||||
// paths are included in `filter`. If `filter` is nil, then all files are included.
|
// paths are included in `filter`. If `filter` is nil, then all files are included.
|
||||||
func TarFilter(path string, compression Compression, filter []string) (io.Reader, error) {
|
func TarFilter(path string, compression Compression, filter []string, recursive bool, createFiles []string) (io.Reader, error) {
|
||||||
args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path}
|
args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path, "-T", "-",}
|
||||||
if filter == nil {
|
if filter == nil {
|
||||||
filter = []string{"."}
|
filter = []string{"."}
|
||||||
}
|
}
|
||||||
args = append(args, "-c"+compression.Flag())
|
args = append(args, "-c"+compression.Flag())
|
||||||
for _, f := range filter {
|
|
||||||
args = append(args, f)
|
if !recursive {
|
||||||
|
args = append(args, "--no-recursion")
|
||||||
}
|
}
|
||||||
return CmdStream(exec.Command(args[0], args[1:]...))
|
|
||||||
|
files := ""
|
||||||
|
for _, f := range filter {
|
||||||
|
files = files + escapeName(f) + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpDir := ""
|
||||||
|
|
||||||
|
if createFiles != nil {
|
||||||
|
tmpDir, err := ioutil.TempDir("", "docker-tar")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
files = files + "-C" + tmpDir + "\n"
|
||||||
|
for _, f := range createFiles {
|
||||||
|
path := filepath.Join(tmpDir, f)
|
||||||
|
err := os.MkdirAll(filepath.Dir(path), 0600)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if file, err := os.OpenFile(path, os.O_CREATE, 0600); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
files = files + escapeName(f) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CmdStream(exec.Command(args[0], args[1:]...), &files, func () {
|
||||||
|
if tmpDir != "" {
|
||||||
|
_ = os.RemoveAll(tmpDir)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
|
// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
|
||||||
|
|
@ -141,7 +193,7 @@ func Untar(archive io.Reader, path string) error {
|
||||||
// TarUntar aborts and returns the error.
|
// TarUntar aborts and returns the error.
|
||||||
func TarUntar(src string, filter []string, dst string) error {
|
func TarUntar(src string, filter []string, dst string) error {
|
||||||
utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
|
utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
|
||||||
archive, err := TarFilter(src, Uncompressed, filter)
|
archive, err := TarFilter(src, Uncompressed, filter, true, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +280,18 @@ func CopyFileWithTar(src, dst string) error {
|
||||||
// CmdStream executes a command, and returns its stdout as a stream.
|
// CmdStream executes a command, and returns its stdout as a stream.
|
||||||
// If the command fails to run or doesn't complete successfully, an error
|
// If the command fails to run or doesn't complete successfully, an error
|
||||||
// will be returned, including anything written on stderr.
|
// will be returned, including anything written on stderr.
|
||||||
func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
|
func CmdStream(cmd *exec.Cmd, input *string, atEnd func()) (io.Reader, error) {
|
||||||
|
if input != nil {
|
||||||
|
stdin, err := cmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Write stdin if any
|
||||||
|
go func() {
|
||||||
|
_, _ = stdin.Write([]byte(*input))
|
||||||
|
stdin.Close()
|
||||||
|
}()
|
||||||
|
}
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -259,6 +322,9 @@ func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
|
||||||
} else {
|
} else {
|
||||||
pipeW.Close()
|
pipeW.Close()
|
||||||
}
|
}
|
||||||
|
if atEnd != nil {
|
||||||
|
atEnd()
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
// Run the command and return the pipe
|
// Run the command and return the pipe
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
|
|
||||||
func TestCmdStreamLargeStderr(t *testing.T) {
|
func TestCmdStreamLargeStderr(t *testing.T) {
|
||||||
cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
|
cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
|
||||||
out, err := CmdStream(cmd)
|
out, err := CmdStream(cmd, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to start command: %s", err)
|
t.Fatalf("Failed to start command: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ func TestCmdStreamLargeStderr(t *testing.T) {
|
||||||
|
|
||||||
func TestCmdStreamBad(t *testing.T) {
|
func TestCmdStreamBad(t *testing.T) {
|
||||||
badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
|
badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
|
||||||
out, err := CmdStream(badCmd)
|
out, err := CmdStream(badCmd, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to start command: %s", err)
|
t.Fatalf("Failed to start command: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ func TestCmdStreamBad(t *testing.T) {
|
||||||
|
|
||||||
func TestCmdStreamGood(t *testing.T) {
|
func TestCmdStreamGood(t *testing.T) {
|
||||||
cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
|
cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
|
||||||
out, err := CmdStream(cmd)
|
out, err := CmdStream(cmd, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1277,5 +1277,5 @@ func (container *Container) Copy(resource string) (Archive, error) {
|
||||||
filter = []string{path.Base(basePath)}
|
filter = []string{path.Base(basePath)}
|
||||||
basePath = path.Dir(basePath)
|
basePath = path.Dir(basePath)
|
||||||
}
|
}
|
||||||
return TarFilter(basePath, Uncompressed, filter)
|
return TarFilter(basePath, Uncompressed, filter, true, nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue