# README
stream
The stream
package contains an implementation of
v1.Layer
that supports streaming access, i.e. the layer contents are read once and not
buffered.
Usage
package main
import (
"os"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/stream"
)
// upload the contents of stdin as a layer to a local registry
func main() {
repo, err := name.NewRepository("localhost:5000/stream")
if err != nil {
panic(err)
}
layer := stream.NewLayer(os.Stdin)
if err := remote.WriteLayer(repo, layer); err != nil {
panic(err)
}
}
Structure
This implements the layer portion of an image
upload. We launch a goroutine that
is responsible for hashing the uncompressed contents to compute the DiffID
,
gzipping them to produce the Compressed
contents, and hashing/counting the
bytes to produce the Digest
/Size
. This goroutine writes to an
io.PipeWriter
, which blocks until Compressed
reads the gzipped contents from
the corresponding io.PipeReader
.
Caveats
This assumes that you have an uncompressed layer (i.e. a tarball) and would like
to compress it. Calling Uncompressed
is always an error. Likewise, other
methods are invalid until the contents of Compressed
have been completely
consumed and Close
d.
Using a stream.Layer
will likely not work without careful consideration. For
example, in the mutate
package, we defer computing the manifest and config
file until they are actually called. This allows you to mutate.Append
a
streaming layer to an image without accidentally consuming it. Similarly, in
remote.Write
, if calling Digest
on a layer fails, we attempt to upload the
layer anyway, understanding that we may be dealing with a stream.Layer
whose
contents need to be uploaded before we can upload the config file.
Given the structure of how this is implemented, forgetting to
Close
a stream.Layer
will leak a goroutine.