read gzip archives
This commit is contained in:
parent
a45e512697
commit
fd244bfc23
@ -3,6 +3,8 @@ package tartest
|
|||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -53,6 +55,20 @@ func (tb Tarball) Buffer() *bytes.Buffer {
|
|||||||
return &buf
|
return &buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gzip returns a gzipped buffer
|
||||||
|
func (tb Tarball) Gzip() *bytes.Buffer {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w := gzip.NewWriter(&buf)
|
||||||
|
_, err := io.Copy(w, tb.Buffer())
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("Gzip(): %w", err))
|
||||||
|
}
|
||||||
|
if err := w.Close(); err != nil {
|
||||||
|
panic(fmt.Errorf("gzip.Close(): %w", err))
|
||||||
|
}
|
||||||
|
return &buf
|
||||||
|
}
|
||||||
|
|
||||||
// Tar tars the Dir
|
// Tar tars the Dir
|
||||||
func (d Dir) Tar(tw *tar.Writer) error {
|
func (d Dir) Tar(tw *tar.Writer) error {
|
||||||
hdr := &tar.Header{
|
hdr := &tar.Header{
|
||||||
|
@ -2,9 +2,12 @@ package tartest
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTarball(t *testing.T) {
|
func TestTarball(t *testing.T) {
|
||||||
@ -23,6 +26,18 @@ func TestTarball(t *testing.T) {
|
|||||||
Dir{Name: "bin"},
|
Dir{Name: "bin"},
|
||||||
Hardlink{Name: "entrypoint2"},
|
Hardlink{Name: "entrypoint2"},
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, want, got)
|
assert.Equal(t, want, got)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGzip(t *testing.T) {
|
||||||
|
tb := Tarball{File{Name: "entrypoint.sh", Contents: bytes.NewBufferString("hello")}}
|
||||||
|
tbuf := tb.Buffer()
|
||||||
|
|
||||||
|
tgz, err := gzip.NewReader(tb.Gzip())
|
||||||
|
require.NoError(t, err)
|
||||||
|
var uncompressed bytes.Buffer
|
||||||
|
_, err = io.Copy(&uncompressed, tgz)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tbuf.Bytes(), uncompressed.Bytes())
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -2,6 +2,8 @@ package rootfs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -9,7 +11,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/motiejus/code/undocker/internal/bytecounter"
|
"github.com/motiejus/code/undocker/internal/bytecounter"
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,6 +45,7 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
wr := bytecounter.New(w)
|
wr := bytecounter.New(w)
|
||||||
tr := tar.NewReader(r.rd)
|
tr := tar.NewReader(r.rd)
|
||||||
tw := tar.NewWriter(wr)
|
tw := tar.NewWriter(wr)
|
||||||
|
var closer func() error
|
||||||
defer func() {
|
defer func() {
|
||||||
err = multierr.Append(err, tw.Close())
|
err = multierr.Append(err, tw.Close())
|
||||||
n = wr.N
|
n = wr.N
|
||||||
@ -110,7 +113,7 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
if _, err := r.rd.Seek(no.offset, io.SeekStart); err != nil {
|
if _, err := r.rd.Seek(no.offset, io.SeekStart); err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
tr = tar.NewReader(r.rd)
|
tr, closer = readTar(r.rd)
|
||||||
for {
|
for {
|
||||||
hdr, err := tr.Next()
|
hdr, err := tr.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@ -138,9 +141,11 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file2layer[hdr.Name] = i
|
file2layer[hdr.Name] = i
|
||||||
}
|
}
|
||||||
|
if err := closer(); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// construct directories to whiteout, for each layer.
|
// construct directories to whiteout, for each layer.
|
||||||
@ -151,7 +156,7 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
if _, err := r.rd.Seek(no.offset, io.SeekStart); err != nil {
|
if _, err := r.rd.Seek(no.offset, io.SeekStart); err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
tr = tar.NewReader(r.rd)
|
tr, closer = readTar(r.rd)
|
||||||
for {
|
for {
|
||||||
hdr, err := tr.Next()
|
hdr, err := tr.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@ -173,6 +178,9 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := closer(); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
@ -223,3 +231,31 @@ func whiteoutDirs(whreaddir map[string]int, nlayers int) []*tree {
|
|||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readTar creates a tar reader from a targzip or tar
|
||||||
|
func readTar(r io.Reader) (*tar.Reader, func() error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w := &discarder{w: &buf}
|
||||||
|
r2 := io.TeeReader(r, w)
|
||||||
|
gz, err := gzip.NewReader(r2)
|
||||||
|
if err == nil {
|
||||||
|
w.discard = true
|
||||||
|
buf.Reset()
|
||||||
|
return tar.NewReader(gz), gz.Close
|
||||||
|
}
|
||||||
|
return tar.NewReader(io.MultiReader(&buf, r)), func() error { return nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
// discarder is a pass-through writer until asked to 'discard' its writes.
|
||||||
|
// useful for proxying writes from a TeeReader until a certain point.
|
||||||
|
type discarder struct {
|
||||||
|
w io.Writer
|
||||||
|
discard bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *discarder) Write(p []byte) (int, error) {
|
||||||
|
if d.discard {
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
return d.w.Write(p)
|
||||||
|
}
|
||||||
|
@ -167,6 +167,18 @@ func TestRootFS(t *testing.T) {
|
|||||||
file{Name: "a/fileb"},
|
file{Name: "a/fileb"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "archived layer",
|
||||||
|
image: tarball{
|
||||||
|
file{Name: "layer1/layer.tar", Contents: layer1.Gzip()},
|
||||||
|
file{Name: "layer0/layer.tar", Contents: layer0.Gzip()},
|
||||||
|
manifest{"layer0/layer.tar", "layer1/layer.tar"},
|
||||||
|
},
|
||||||
|
want: []extractable{
|
||||||
|
dir{Name: "/", UID: 0},
|
||||||
|
file{Name: "/file", UID: 1, Contents: bytes.NewBufferString("from 1")},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
Loading…
Reference in New Issue
Block a user