Skip to content

Commit

Permalink
Add oci-archive transport that creates a tar archive of an image
Browse files Browse the repository at this point in the history
Signed-off-by: umohnani8 <umohnani@redhat.com>
  • Loading branch information
umohnani8 committed Jul 28, 2017
1 parent 1066078 commit 5c221e4
Show file tree
Hide file tree
Showing 5 changed files with 332 additions and 1 deletion.
128 changes: 128 additions & 0 deletions oci/archive/oci_dest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package archive

import (
"io"
"os"

"github.com/containers/image/oci/layout"
"github.com/containers/image/types"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
)

type ociArchiveImageDestination struct {
ref ociArchiveReference
ociImgDest types.ImageDestination
}

// newImageDestination returns an ImageDestination for writing to an existing directory.
func newImageDestination(ref ociArchiveReference, ctx *types.SystemContext) (types.ImageDestination, error) {
ociImgDest, err := ref.ociRef.NewImageDestination(ctx)
if err != nil {
return nil, err
}
return &ociArchiveImageDestination{ref: ref,
ociImgDest: ociImgDest}, nil
}

// Reference returns the reference used to set up this destination.
func (d *ociArchiveImageDestination) Reference() types.ImageReference {
return d.ociImgDest.Reference()
}

// Close removes resources associated with an initialized ImageDestination, if any
// Close deleted the temp directory of the oci-archive image
func (d *ociArchiveImageDestination) Close() error {
_, tmpDir, _ := layout.GetOciReference(d.ref.ociRef)
defer os.RemoveAll(tmpDir)
return d.ociImgDest.Close()
}

func (d *ociArchiveImageDestination) SupportedManifestMIMETypes() []string {
return d.ociImgDest.SupportedManifestMIMETypes()
}

// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures
func (d *ociArchiveImageDestination) SupportsSignatures() error {
return d.ociImgDest.SupportsSignatures()
}

// ShouldCompressLayers returns true iff it is desirable to compress layer blobs written to this destination
func (d *ociArchiveImageDestination) ShouldCompressLayers() bool {
return d.ociImgDest.ShouldCompressLayers()
}

// AcceptsForeignLayerURLs returns false iff foreign layers in manifest should be actually
// uploaded to the image destination, true otherwise.
func (d *ociArchiveImageDestination) AcceptsForeignLayerURLs() bool {
return d.ociImgDest.AcceptsForeignLayerURLs()
}

// MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise
func (d *ociArchiveImageDestination) MustMatchRuntimeOS() bool {
return d.ociImgDest.MustMatchRuntimeOS()
}

// PutBlob writes contents of stream and returns data representing the result (with all data filled in).
// inputInfo.Digest can be optionally provided if known; it is not mandatory for the implementation to verify it.
// inputInfo.Size is the expected length of stream, if known.
func (d *ociArchiveImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo) (types.BlobInfo, error) {
return d.ociImgDest.PutBlob(stream, inputInfo)
}

// HasBlob returns true iff the image destination already contains a blob with the matching digest which can be reapplied using ReapplyBlob
func (d *ociArchiveImageDestination) HasBlob(info types.BlobInfo) (bool, int64, error) {
return d.ociImgDest.HasBlob(info)
}

func (d *ociArchiveImageDestination) ReapplyBlob(info types.BlobInfo) (types.BlobInfo, error) {
return d.ociImgDest.ReapplyBlob(info)
}

// PutManifest writes manifest to the destination.
func (d *ociArchiveImageDestination) PutManifest(m []byte) error {
return d.ociImgDest.PutManifest(m)
}

func (d *ociArchiveImageDestination) PutSignatures(signatures [][]byte) error {
return d.ociImgDest.PutSignatures(signatures)
}

// Commit marks the process of storing the image as successful and asks for the image to be persisted
// after the directory is made, it is tarred up into a file and the directory is deleted
func (d *ociArchiveImageDestination) Commit() error {
if err := d.ociImgDest.Commit(); err != nil {
return errors.Wrapf(err, "error storing image %q", d.ref.tag)
}

// path of directory to tar up
_, src, _ := layout.GetOciReference(d.ref.ociRef)
// path to save tarred up file
dst := d.ref.resolvedDir
if err := tar(src, dst); err != nil {
return errors.Wrapf(err, "error tarring up directory %q", src)
}

return nil
}

// tar converts the direstory at src and saves it to dst
func tar(src, dst string) error {
// input is a stream of bytes from the archive of the directory at path
input, err := archive.Tar(src, 0)
if err != nil {
return errors.Wrapf(err, "error retrieving stream of bytes from %q", src)
}

// creates a temporary file
outFile, err := os.Create(dst)
if err != nil {
return errors.Wrapf(err, "error creating tar file %q", dst)
}
defer outFile.Close()

// copies the contents of the directory to the tar file
_, err = io.Copy(outFile, input)

return err
}
79 changes: 79 additions & 0 deletions oci/archive/oci_src.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package archive

import (
"io"

"os"

"github.com/Sirupsen/logrus"
"github.com/containers/image/oci/layout"
"github.com/containers/image/types"
"github.com/containers/storage/pkg/archive"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)

type ociArchiveImageSource struct {
ref ociArchiveReference
ociImgSrc types.ImageSource
}

// newImageSource returns an ImageSource for reading from an existing directory.
// newImageSource untars the file and saves it in a temp directory
func newImageSource(ref ociArchiveReference, ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
// src is the path of the tarred file
src := ref.resolvedDir
// dst is the temp path of the directory after untarring the file
_, dst, _ := layout.GetOciReference(ref.ociRef)
if err := unTar(src, dst); err != nil {
return nil, errors.Wrapf(err, "error untarring file %q", src)
}

ociImgSrc, err := ref.ociRef.NewImageSource(ctx, requestedManifestMIMETypes)
if err != nil {
return nil, err
}
return &ociArchiveImageSource{ref: ref,
ociImgSrc: ociImgSrc}, nil
}

// Reference returns the reference used to set up this source.
func (s *ociArchiveImageSource) Reference() types.ImageReference {
return s.ociImgSrc.Reference()
}

// Close removes resources associated with an initialized ImageSource, if any.
// Close deletes the temporary directory at dst
func (s *ociArchiveImageSource) Close() error {
_, dst, _ := layout.GetOciReference(s.ref.ociRef)
defer os.RemoveAll(dst)
return s.ociImgSrc.Close()
}

// GetManifest returns the image's manifest along with its MIME type
// (which may be empty when it can't be determined but the manifest is available).
func (s *ociArchiveImageSource) GetManifest() ([]byte, string, error) {
return s.ociImgSrc.GetManifest()
}

func (s *ociArchiveImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) {
return s.ociImgSrc.GetTargetManifest(digest)
}

// GetBlob returns a stream for the specified blob, and the blob's size.
func (s *ociArchiveImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) {
return s.ociImgSrc.GetBlob(info)
}

func (s *ociArchiveImageSource) GetSignatures() ([][]byte, error) {
return s.ociImgSrc.GetSignatures()
}

// unTar unpacks the tar file at src and saves it at dst
func unTar(src, dst string) error {
if err := archive.UntarPath(src, dst); err != nil {
logrus.Info("error untarring file")
return errors.Wrapf(err, "error untarring file %q", src)
}
return nil
}
117 changes: 117 additions & 0 deletions oci/archive/oci_transport.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package archive

import (
"github.com/containers/image/docker/reference"
"github.com/containers/image/image"
"github.com/containers/image/oci/layout"
"github.com/containers/image/transports"
"github.com/containers/image/types"
"github.com/pkg/errors"
)

func init() {
transports.Register(Transport)
}

// Transport is an ImageTransport for OCI archive
// it creates an oci-archive tar file by calling into the OCI transport
// tarring the directory created by oci and deleting the directory
var Transport = ociArchiveTransport{}

type ociArchiveTransport struct{}

// ociArchiveReference is an ImageReference for OCI Archive paths
type ociArchiveReference struct {
dir string
resolvedDir string
tag string
ociRef types.ImageReference
}

func (t ociArchiveTransport) Name() string {
return "oci-archive"
}

// ParseReference converts a string, which should not start with the ImageTransport.Name prefix
// into an ImageReference.
func (t ociArchiveTransport) ParseReference(reference string) (types.ImageReference, error) {
// ociArchRef stores the dir, resolvedDir, and tag of the original path given by the user
ociArchRef, err := layout.Transport.ParseReference(reference)
if err != nil {
return nil, errors.Wrapf(err, "error parsing reference %q", reference)
}
dir, resolvedDir, tag := layout.GetOciReference(ociArchRef)

// tempRef is the path where the directory of the oci image created by oci is stored
// this directory is deleted after a tar file is created from it
tempRef := "/var/tmp/oci-image:" + tag
// ociRef stores the dir, resolvedDir, and tag of the temporary path
ociRef, err := layout.Transport.ParseReference(tempRef)
if err != nil {
return nil, errors.Wrapf(err, "error parsing temp reference %q", tempRef)
}
return ociArchiveReference{dir: dir,
resolvedDir: resolvedDir,
tag: tag,
ociRef: ociRef,
}, nil
}

// ValidatePolicyConfigurationScope checks that scope is a valid name for a signature.PolicyTransportScopes keys
func (t ociArchiveTransport) ValidatePolicyConfigurationScope(scope string) error {
return layout.Transport.ValidatePolicyConfigurationScope(scope)
}

func (ref ociArchiveReference) Transport() types.ImageTransport {
return Transport
}

// StringWithinTransport returns a string representation of the reference, which MUST be such that
// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference.
func (ref ociArchiveReference) StringWithinTransport() string {
return ref.ociRef.StringWithinTransport()
}

// DockerReference returns a Docker reference associated with this reference
func (ref ociArchiveReference) DockerReference() reference.Named {
return nil
}

// PolicyConfigurationIdentity returns a string representation of the reference, suitable for policy lookup.
func (ref ociArchiveReference) PolicyConfigurationIdentity() string {
return ref.ociRef.PolicyConfigurationIdentity()
}

// PolicyConfigurationNamespaces returns a list of other policy configuration namespaces to search
// for if explicit configuration for PolicyConfigurationIdentity() is not set
func (ref ociArchiveReference) PolicyConfigurationNamespaces() []string {
return ref.ociRef.PolicyConfigurationNamespaces()
}

// NewImage returns a types.Image for this reference, possibly specialized for this ImageTransport.
// The caller must call .Close() on the returned Image.
func (ref ociArchiveReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
src, err := newImageSource(ref, ctx, nil)
if err != nil {
return nil, err
}
return image.FromSource(src)
}

// NewImageSource returns a types.ImageSource for this reference,
// asking the backend to use a manifest from requestedManifestMIMETypes if possible.
// The caller must call .Close() on the returned ImageSource.
func (ref ociArchiveReference) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
return newImageSource(ref, ctx, requestedManifestMIMETypes)
}

// NewImageDestination returns a types.ImageDestination for this reference.
// The caller must call .Close() on the returned ImageDestination.
func (ref ociArchiveReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
return newImageDestination(ref, ctx)
}

// DeleteImage deletes the named image from the registry, if supported.
func (ref ociArchiveReference) DeleteImage(ctx *types.SystemContext) error {
return ref.ociRef.DeleteImage(ctx)
}
8 changes: 7 additions & 1 deletion oci/layout/oci_transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/containers/image/image"
"github.com/containers/image/transports"
"github.com/containers/image/types"
"github.com/opencontainers/go-digest"
digest "github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -88,6 +88,12 @@ type ociReference struct {
tag string
}

// GetOciReference returns the dir, resolvedDir, and tag of ociReference
// so that it is accessible in oci/archive
func GetOciReference(t types.ImageReference) (string, string, string) {
return t.(ociReference).dir, t.(ociReference).resolvedDir, t.(ociReference).tag
}

// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OCI ImageReference.
func ParseReference(reference string) (types.ImageReference, error) {
var dir, tag string
Expand Down
1 change: 1 addition & 0 deletions transports/alltransports/alltransports.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
_ "github.com/containers/image/docker"
_ "github.com/containers/image/docker/archive"
_ "github.com/containers/image/docker/daemon"
_ "github.com/containers/image/oci/archive"
_ "github.com/containers/image/oci/layout"
_ "github.com/containers/image/openshift"
// The ostree transport is registered by ostree*.go
Expand Down

0 comments on commit 5c221e4

Please sign in to comment.