remove ByteCounter from rootfs

This commit is contained in:
Motiejus Jakštys 2021-05-24 00:11:58 +03:00
parent e1555f31a8
commit e2cf760c12
4 changed files with 29 additions and 31 deletions

View File

@ -13,7 +13,11 @@ import (
) )
type ( type (
rootfsFactory func(io.ReadSeeker) io.WriterTo flattener interface {
Flatten(io.Writer) error
}
rootfsFactory func(io.ReadSeeker) flattener
// Command is "rootfs" command // Command is "rootfs" command
Command struct { Command struct {
@ -55,10 +59,7 @@ func (c *Command) Execute(args []string) (err error) {
out = outf out = outf
} }
if _, err := c.rootfsNew(rd).WriteTo(out); err != nil { return c.rootfsNew(rd).Flatten(out)
return err
}
return nil
} }
// init() initializes Command with the default options. // init() initializes Command with the default options.
@ -67,7 +68,7 @@ func (c *Command) Execute(args []string) (err error) {
// command will initialize itself. // command will initialize itself.
func (c *Command) init() { func (c *Command) init() {
c.BaseCommand.Init() c.BaseCommand.Init()
c.rootfsNew = func(r io.ReadSeeker) io.WriterTo { c.rootfsNew = func(r io.ReadSeeker) flattener {
return rootfs.New(r) return rootfs.New(r)
} }
} }

View File

@ -67,7 +67,7 @@ func TestExecute(t *testing.T) {
inf := filepath.Join(dir, tt.infile) inf := filepath.Join(dir, tt.infile)
c.PositionalArgs.Infile = goflags.Filename(inf) c.PositionalArgs.Infile = goflags.Filename(inf)
c.PositionalArgs.Outfile = tt.outfile c.PositionalArgs.Outfile = tt.outfile
c.rootfsNew = func(r io.ReadSeeker) io.WriterTo { c.rootfsNew = func(r io.ReadSeeker) flattener {
return &passthrough{r} return &passthrough{r}
} }
@ -92,4 +92,7 @@ func TestExecute(t *testing.T) {
type passthrough struct{ r io.Reader } type passthrough struct{ r io.Reader }
func (p *passthrough) WriteTo(w io.Writer) (int64, error) { return io.Copy(w, p.r) } func (p *passthrough) Flatten(w io.Writer) error {
_, err := io.Copy(w, p.r)
return err
}

View File

@ -12,7 +12,6 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/motiejus/code/undocker/internal/bytecounter"
"go.uber.org/multierr" "go.uber.org/multierr"
) )
@ -48,8 +47,8 @@ func New(rd io.ReadSeeker) *RootFS {
return &RootFS{rd: rd} return &RootFS{rd: rd}
} }
// WriteTo writes a docker image to an open tarball. // Flatten flattens a docker image to an open tarball.
func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) { func (r *RootFS) Flatten(w io.Writer) (err error) {
tr := tar.NewReader(r.rd) tr := tar.NewReader(r.rd)
var closer func() error var closer func() error
@ -72,19 +71,19 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
case filepath.Clean(hdr.Name) == _manifestJSON: case filepath.Clean(hdr.Name) == _manifestJSON:
dec := json.NewDecoder(tr) dec := json.NewDecoder(tr)
if err := dec.Decode(&manifest); err != nil { if err := dec.Decode(&manifest); err != nil {
return n, fmt.Errorf("decode %s: %w", _manifestJSON, err) return fmt.Errorf("decode %s: %w", _manifestJSON, err)
} }
case strings.HasSuffix(hdr.Name, _layerSuffix): case strings.HasSuffix(hdr.Name, _layerSuffix):
here, err := r.rd.Seek(0, io.SeekCurrent) here, err := r.rd.Seek(0, io.SeekCurrent)
if err != nil { if err != nil {
return n, err return err
} }
layerOffsets[hdr.Name] = here layerOffsets[hdr.Name] = here
} }
} }
if len(manifest) == 0 || len(layerOffsets) != len(manifest[0].Layers) { if len(manifest) == 0 || len(layerOffsets) != len(manifest[0].Layers) {
return n, errBadManifest return errBadManifest
} }
// enumerate layers the way they would be laid down in the image // enumerate layers the way they would be laid down in the image
@ -109,7 +108,7 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
// iterate over all files, construct `file2layer`, `whreaddir`, `wh` // iterate over all files, construct `file2layer`, `whreaddir`, `wh`
for i, no := range layers { for i, no := range layers {
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 err
} }
tr, closer = openTargz(r.rd) tr, closer = openTargz(r.rd)
for { for {
@ -118,7 +117,7 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
break break
} }
if err != nil { if err != nil {
return n, fmt.Errorf("decode %s: %w", no.name, err) return fmt.Errorf("decode %s: %w", no.name, err)
} }
if hdr.Typeflag == tar.TypeDir { if hdr.Typeflag == tar.TypeDir {
continue continue
@ -142,23 +141,21 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
file2layer[hdr.Name] = i file2layer[hdr.Name] = i
} }
if err := closer(); err != nil { if err := closer(); err != nil {
return n, err return err
} }
} }
// construct directories to whiteout, for each layer. // construct directories to whiteout, for each layer.
whIgnore := whiteoutDirs(whreaddir, len(layers)) whIgnore := whiteoutDirs(whreaddir, len(layers))
wr := bytecounter.New(w) tw := tar.NewWriter(w)
tw := tar.NewWriter(wr)
defer func() { defer func() {
err = multierr.Append(err, tw.Close()) err = multierr.Append(err, tw.Close())
n = wr.N
}() }()
// iterate through all layers, all files, and write files. // iterate through all layers, all files, and write files.
for i, no := range layers { for i, no := range layers {
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 err
} }
tr, closer = openTargz(r.rd) tr, closer = openTargz(r.rd)
for { for {
@ -167,7 +164,7 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
break break
} }
if err != nil { if err != nil {
return n, fmt.Errorf("decode %s: %w", no.name, err) return fmt.Errorf("decode %s: %w", no.name, err)
} }
if layer, ok := wh[hdr.Name]; ok && layer >= i { if layer, ok := wh[hdr.Name]; ok && layer >= i {
continue continue
@ -179,14 +176,14 @@ func (r *RootFS) WriteTo(w io.Writer) (n int64, err error) {
continue continue
} }
if err := writeFile(tr, tw, hdr); err != nil { if err := writeFile(tr, tw, hdr); err != nil {
return n, err return err
} }
} }
if err := closer(); err != nil { if err := closer(); err != nil {
return n, err return err
} }
} }
return n, nil return nil
} }
func writeFile(tr *tar.Reader, tw *tar.Writer, hdr *tar.Header) error { func writeFile(tr *tar.Reader, tw *tar.Writer, hdr *tar.Header) error {
@ -238,10 +235,8 @@ func whiteoutDirs(whreaddir map[string]int, nlayers int) []*tree {
// openTargz creates a tar reader from a targzip or tar. // openTargz creates a tar reader from a targzip or tar.
// //
// We may be looking at magic values for tar and/or gzip, // It will try to open a gzip stream, and, if that fails, silently fall back to
// which would mean "cleaner" code (e.g. no proxyWriter), // tar. I will accept a cleaner implementation looking at magic values.
// but that would mean re-implementing gzip.readHeader(),
// which is ... already in stdlib.
func openTargz(r io.Reader) (*tar.Reader, func() error) { func openTargz(r io.Reader) (*tar.Reader, func() error) {
hdrbuf := &bytes.Buffer{} hdrbuf := &bytes.Buffer{}
hdrw := &proxyWriter{w: hdrbuf} hdrw := &proxyWriter{w: hdrbuf}

View File

@ -186,7 +186,7 @@ func TestRootFS(t *testing.T) {
in := bytes.NewReader(tt.image.Buffer().Bytes()) in := bytes.NewReader(tt.image.Buffer().Bytes())
out := bytes.Buffer{} out := bytes.Buffer{}
n, err := New(in).WriteTo(&out) err := New(in).Flatten(&out)
if tt.wantErr != "" { if tt.wantErr != "" {
assert.EqualError(t, err, tt.wantErr) assert.EqualError(t, err, tt.wantErr)
return return
@ -195,7 +195,6 @@ func TestRootFS(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
got := tartest.Extract(t, bytes.NewReader(outb)) got := tartest.Extract(t, bytes.NewReader(outb))
assert.Equal(t, tt.want, got) assert.Equal(t, tt.want, got)
assert.Equal(t, int64(len(outb)), n)
}) })
} }
} }