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 Aug 8, 2017
1 parent 74e3593 commit bab93fc
Show file tree
Hide file tree
Showing 5 changed files with 548 additions and 0 deletions.
188 changes: 188 additions & 0 deletions oci/archive/oci_dest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package archive

import (
"io"
"os"

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

type ociArchiveImageDestination struct {
ref ociArchiveReference
unpackedDest types.ImageDestination
tempDir string
}

// newImageDestination returns an ImageDestination for writing to an existing directory.
func newImageDestination(ref ociArchiveReference, ctx *types.SystemContext) (types.ImageDestination, error) {
tempDirOciRef, err := createOciRef(ref.image)
if err != nil {
return nil, errors.Wrapf(err, "error creating oci reference")
}
unpackedDest, err := tempDirOciRef.ociRef.NewImageDestination(ctx)
if err != nil {
if err := deleteTempDir(); err != nil {
return nil, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return nil, err
}
return &ociArchiveImageDestination{ref: ref,
unpackedDest: unpackedDest,
tempDir: tempDirOciRef.tempDirectory}, nil
}

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

// 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 {
if err := d.unpackedDest.Close(); err != nil {
if err := deleteTempDir(); err != nil {
return errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return errors.Wrapf(err, "error closing image destination")
}
if err := deleteTempDir(); err != nil {
return errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return nil
}

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

// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures
func (d *ociArchiveImageDestination) SupportsSignatures() error {
err := d.unpackedDest.SupportsSignatures()
if err != nil {
if err := deleteTempDir(); err != nil {
return errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return err
}
return nil
}

// ShouldCompressLayers returns true iff it is desirable to compress layer blobs written to this destination
func (d *ociArchiveImageDestination) ShouldCompressLayers() bool {
return d.unpackedDest.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.unpackedDest.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.unpackedDest.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) {
valBlobInfo, err := d.unpackedDest.PutBlob(stream, inputInfo)
if err != nil {
if err := deleteTempDir(); err != nil {
return types.BlobInfo{}, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return types.BlobInfo{}, err
}
return valBlobInfo, nil
}

// 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) {
valBool, valInt, err := d.unpackedDest.HasBlob(info)
if err != nil {
if err := deleteTempDir(); err != nil {
return false, -1, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return false, -1, err
}
return valBool, valInt, nil
}

func (d *ociArchiveImageDestination) ReapplyBlob(info types.BlobInfo) (types.BlobInfo, error) {
valBlobInfo, err := d.unpackedDest.ReapplyBlob(info)
if err != nil {
if err := deleteTempDir(); err != nil {
return types.BlobInfo{}, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return types.BlobInfo{}, err
}
return valBlobInfo, nil
}

// PutManifest writes manifest to the destination
func (d *ociArchiveImageDestination) PutManifest(m []byte) error {
err := d.unpackedDest.PutManifest(m)
if err != nil {
if err := deleteTempDir(); err != nil {
return errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return err
}
return nil
}

func (d *ociArchiveImageDestination) PutSignatures(signatures [][]byte) error {
err := d.unpackedDest.PutSignatures(signatures)
if err != nil {
if err := deleteTempDir(); err != nil {
return errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return err
}
return nil
}

// 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.unpackedDest.Commit(); err != nil {
if err := deleteTempDir(); err != nil {
return errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return errors.Wrapf(err, "error storing image %q", d.ref.image)
}

// path of directory to tar up
src := d.tempDir
// path to save tarred up file
dst := d.ref.resolvedFile
if err := tar(src, dst); err != nil {
return err
}

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, archive.Uncompressed)
if err != nil {
return errors.Wrapf(err, "error retrieving stream of bytes from %q", src)
}

// creates the tar 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
}
153 changes: 153 additions & 0 deletions oci/archive/oci_src.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package archive

import (
"context"
"io"

"os"

ocilayout "github.com/containers/image/oci/layout"
"github.com/containers/image/types"
"github.com/containers/storage/pkg/archive"
digest "github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)

type ociArchiveImageSource struct {
ref ociArchiveReference
unpackedSrc types.ImageSource
tempDir string
}

var tempDirRef *tempDirOciRef

// 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) {
if tempDirRef == nil {
if err := createUntarTempDir(ref); err != nil {
return nil, errors.Wrap(err, "error creating temp directory")
}
}
unpackedSrc, err := tempDirRef.ociRef.NewImageSource(ctx, requestedManifestMIMETypes)
if err != nil {
if err := deleteTempDir(); err != nil {
return nil, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return nil, err
}
return &ociArchiveImageSource{ref: ref,
unpackedSrc: unpackedSrc,
tempDir: tempDirRef.tempDirectory}, nil
}

// LoadManifestDescriptor loads the manifest
func LoadManifestDescriptor(imgRef types.ImageReference) (imgspecv1.Descriptor, error) {
if tempDirRef == nil {
if err := createUntarTempDir(imgRef.(ociArchiveReference)); err != nil {
return imgspecv1.Descriptor{}, errors.Wrap(err, "error creating temp directory")
}
}
descriptor, err := ocilayout.LoadManifestDescriptor(tempDirRef.ociRef)
if err != nil {
if err := deleteTempDir(); err != nil {
return imgspecv1.Descriptor{}, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return imgspecv1.Descriptor{}, errors.Wrap(err, "error loading manifest")
}
return descriptor, nil
}

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

// Close removes resources associated with an initialized ImageSource, if any.
// Close deletes the temporary directory at dst
func (s *ociArchiveImageSource) Close() error {
if err := s.unpackedSrc.Close(); err != nil {
if err := deleteTempDir(); err != nil {
return errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return errors.Wrapf(err, "error closing image source")
}
if err := deleteTempDir(); err != nil {
return errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return nil
}

// 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.unpackedSrc.GetManifest()
}

func (s *ociArchiveImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string, error) {
valByte, valString, err := s.unpackedSrc.GetTargetManifest(digest)
if err != nil {
if err := deleteTempDir(); err != nil {
return nil, "", errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return nil, "", err
}
return valByte, valString, nil
}

// GetBlob returns a stream for the specified blob, and the blob's size.
func (s *ociArchiveImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) {
ioReader, valInt, err := s.unpackedSrc.GetBlob(info)
if err != nil {
if err := deleteTempDir(); err != nil {
return nil, 0, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return nil, 0, err
}
return ioReader, valInt, nil
}

func (s *ociArchiveImageSource) GetSignatures(c context.Context) ([][]byte, error) {
valByte, err := s.unpackedSrc.GetSignatures(c)
if err != nil {
if err := deleteTempDir(); err != nil {
return nil, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory)
}
return nil, err
}
return valByte, nil
}

// 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 {
return errors.Wrapf(err, "error untarring file %q", src)
}
return nil
}

// deleteTemDir deletes the temporary directory created for the oci image
func deleteTempDir() error {
if tempDirRef != nil {
return os.RemoveAll(tempDirRef.tempDirectory)
}
return nil
}

// creates the temporary directory and copies the tarred content to it
func createUntarTempDir(ref ociArchiveReference) error {
var err error
tempDirRef, err = createOciRef(ref.image)
if err != nil {
return errors.Wrap(err, "error creating oci reference")
}
src := ref.resolvedFile
dst := tempDirRef.tempDirectory
if err := unTar(src, dst); err != nil {
if err := deleteTempDir(); err != nil {
return errors.Wrapf(err, "error deleting temp directory %q", tempDirRef.tempDirectory)
}
}
return nil
}
Loading

0 comments on commit bab93fc

Please sign in to comment.