Skip to content

Commit

Permalink
Fix crane append for named pipes (#868)
Browse files Browse the repository at this point in the history
* stream.Layer: Don't fail if Close is called twice

Fixes #707

* crane.Append: Use stream.Layer for pipes

It's annoying that `crane append` is hard to use with one-liners, which
is the secret goal of most crane commands. We now stat the layer files
and use a streaming layer for anything that's not a regular file, which
includes named pipes, which fail if we try to open them multiple times.

Now you can do this:

$ crane append -f <(tar -c some-dir/ -f -) -t $IMAGE
  • Loading branch information
jonjohnsonjr committed Dec 12, 2020
1 parent 94913c0 commit a85f8fd
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 6 deletions.
26 changes: 24 additions & 2 deletions pkg/crane/append.go
Expand Up @@ -16,22 +16,44 @@ package crane

import (
"fmt"
"os"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/stream"
"github.com/google/go-containerregistry/pkg/v1/tarball"
)

// Append reads a layer from path and appends it the the v1.Image base.
func Append(base v1.Image, paths ...string) (v1.Image, error) {
layers := make([]v1.Layer, 0, len(paths))
for _, path := range paths {
layer, err := tarball.LayerFromFile(path)
layer, err := getLayer(path)
if err != nil {
return nil, fmt.Errorf("reading tar %q: %v", path, err)
return nil, fmt.Errorf("reading layer %q: %v", path, err)
}

layers = append(layers, layer)
}

return mutate.AppendLayers(base, layers...)
}

func getLayer(path string) (v1.Layer, error) {
fi, err := os.Stat(path)
if err != nil {
return nil, err
}

// If we're dealing with a named pipe, trying to open it multiple times will
// fail, so we need to do a streaming upload.
if !fi.Mode().IsRegular() {
rc, err := os.Open(path)
if err != nil {
return nil, err
}
return stream.NewLayer(rc), nil
}

return tarball.LayerFromFile(path)
}
4 changes: 1 addition & 3 deletions pkg/crane/push.go
Expand Up @@ -36,7 +36,5 @@ func Push(img v1.Image, dst string, opt ...Option) error {
if err != nil {
return fmt.Errorf("parsing tag %q: %v", dst, err)
}
return remote.MultiWrite(map[name.Reference]remote.Taggable{
tag: img,
}, o.remote...)
return remote.Write(tag, img, o.remote...)
}
5 changes: 4 additions & 1 deletion pkg/v1/stream/layer.go
Expand Up @@ -21,6 +21,7 @@ import (
"errors"
"hash"
"io"
"os"
"sync"

v1 "github.com/google/go-containerregistry/pkg/v1"
Expand Down Expand Up @@ -216,7 +217,9 @@ func newMultiCloser(c ...io.Closer) multiCloser { return multiCloser(c) }

func (m multiCloser) Close() error {
for _, c := range m {
if err := c.Close(); err != nil {
// NOTE: net/http will call close on success, so if we've already
// closed the inner rc, it's not an error.
if err := c.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
return err
}
}
Expand Down

0 comments on commit a85f8fd

Please sign in to comment.