diff --git a/pkg/crane/append.go b/pkg/crane/append.go index 5e2b5422b..48f3bb6a0 100644 --- a/pkg/crane/append.go +++ b/pkg/crane/append.go @@ -16,9 +16,11 @@ 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" ) @@ -26,12 +28,32 @@ import ( 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) +} diff --git a/pkg/crane/push.go b/pkg/crane/push.go index 8cddb3665..52f4fc487 100644 --- a/pkg/crane/push.go +++ b/pkg/crane/push.go @@ -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...) } diff --git a/pkg/v1/stream/layer.go b/pkg/v1/stream/layer.go index 03ddfb9a1..ff44f487b 100644 --- a/pkg/v1/stream/layer.go +++ b/pkg/v1/stream/layer.go @@ -21,6 +21,7 @@ import ( "errors" "hash" "io" + "os" "sync" v1 "github.com/google/go-containerregistry/pkg/v1" @@ -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 } }