-
Notifications
You must be signed in to change notification settings - Fork 386
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add oci-archive transport that creates a tar archive of an image
Signed-off-by: umohnani8 <umohnani@redhat.com>
- Loading branch information
Showing
5 changed files
with
548 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.