simplify flattener api

main
Motiejus Jakštys 2021-05-24 00:11:58 +03:00
parent e2cf760c12
commit 9ca0d8019c
5 changed files with 18 additions and 38 deletions

View File

@ -13,11 +13,6 @@ import (
) )
type ( type (
flattener interface {
Flatten(io.Writer) error
}
rootfsFactory func(io.ReadSeeker) flattener
// Command is "rootfs" command // Command is "rootfs" command
Command struct { Command struct {
@ -28,7 +23,7 @@ type (
Outfile string `long:"outfile" description:"Output path, stdout is '-'"` Outfile string `long:"outfile" description:"Output path, stdout is '-'"`
} `positional-args:"yes" required:"yes"` } `positional-args:"yes" required:"yes"`
rootfsNew rootfsFactory flattener func(io.ReadSeeker, io.Writer) error
} }
) )
@ -37,7 +32,7 @@ func (c *Command) Execute(args []string) (err error) {
if len(args) != 0 { if len(args) != 0 {
return errors.New("too many args") return errors.New("too many args")
} }
if c.rootfsNew == nil { if c.flattener == nil {
c.init() c.init()
} }
@ -59,7 +54,7 @@ func (c *Command) Execute(args []string) (err error) {
out = outf out = outf
} }
return c.rootfsNew(rd).Flatten(out) return c.flattener(rd, out)
} }
// init() initializes Command with the default options. // init() initializes Command with the default options.
@ -68,7 +63,5 @@ 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) flattener { c.flattener = rootfs.Flatten
return rootfs.New(r)
}
} }

View File

@ -67,9 +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) flattener { c.flattener = flattenPassthrough
return &passthrough{r}
}
err := c.Execute(nil) err := c.Execute(nil)
if tt.wantErr != "" { if tt.wantErr != "" {
@ -90,9 +88,7 @@ func TestExecute(t *testing.T) {
} }
} }
type passthrough struct{ r io.Reader } func flattenPassthrough(r io.ReadSeeker, w io.Writer) error {
_, err := io.Copy(w, r)
func (p *passthrough) Flatten(w io.Writer) error {
_, err := io.Copy(w, p.r)
return err return err
} }

View File

@ -13,7 +13,6 @@ import (
) )
func TestLXCConfig(t *testing.T) { func TestLXCConfig(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
docker dockerConfig docker dockerConfig

View File

@ -27,11 +27,6 @@ var (
) )
type ( type (
// RootFS accepts a docker layer tarball and flattens it.
RootFS struct {
rd io.ReadSeeker
}
dockerManifestJSON []struct { dockerManifestJSON []struct {
Layers []string `json:"Layers"` Layers []string `json:"Layers"`
} }
@ -42,14 +37,11 @@ type (
} }
) )
// New creates a new RootFS'er. // Flatten flattens a docker image to a tarball. The underlying io.Writer
func New(rd io.ReadSeeker) *RootFS { // should be an open file handle, which the caller is responsible for closing
return &RootFS{rd: rd} // themselves
} func Flatten(rd io.ReadSeeker, w io.Writer) (err error) {
tr := tar.NewReader(rd)
// Flatten flattens a docker image to an open tarball.
func (r *RootFS) Flatten(w io.Writer) (err error) {
tr := tar.NewReader(r.rd)
var closer func() error var closer func() error
// layerOffsets maps a layer name (a9b123c0daa/layer.tar) to it's offset // layerOffsets maps a layer name (a9b123c0daa/layer.tar) to it's offset
@ -74,7 +66,7 @@ func (r *RootFS) Flatten(w io.Writer) (err error) {
return 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 := rd.Seek(0, io.SeekCurrent)
if err != nil { if err != nil {
return err return err
} }
@ -107,10 +99,10 @@ func (r *RootFS) Flatten(w io.Writer) (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 := rd.Seek(no.offset, io.SeekStart); err != nil {
return err return err
} }
tr, closer = openTargz(r.rd) tr, closer = openTargz(rd)
for { for {
hdr, err := tr.Next() hdr, err := tr.Next()
if err == io.EOF { if err == io.EOF {
@ -154,10 +146,10 @@ func (r *RootFS) Flatten(w io.Writer) (err error) {
}() }()
// 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 := rd.Seek(no.offset, io.SeekStart); err != nil {
return err return err
} }
tr, closer = openTargz(r.rd) tr, closer = openTargz(rd)
for { for {
hdr, err := tr.Next() hdr, err := tr.Next()
if err == io.EOF { if err == io.EOF {

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{}
err := New(in).Flatten(&out) err := Flatten(in, &out)
if tt.wantErr != "" { if tt.wantErr != "" {
assert.EqualError(t, err, tt.wantErr) assert.EqualError(t, err, tt.wantErr)
return return