Export as a tar (Note: "-" can be used for stdout):
    $ ctr images export /tmp/oci-busybox.tar docker.io/library/busybox:latest
Import a tar (Note: "-" can be used for stdin):
    $ ctr images import foo/new:latest /tmp/oci-busybox.tar
Note: media types are not converted at the moment: e.g.
  application/vnd.docker.image.rootfs.diff.tar.gzip
  -> application/vnd.oci.image.layer.v1.tar+gzip
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
		
	
		
			
				
	
	
		
			66 lines
		
	
	
		
			1.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			66 lines
		
	
	
		
			1.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package containerd
 | 
						|
 | 
						|
import (
 | 
						|
	"archive/tar"
 | 
						|
	"context"
 | 
						|
	"io"
 | 
						|
 | 
						|
	"github.com/containerd/containerd/content"
 | 
						|
	"github.com/containerd/containerd/images"
 | 
						|
	"github.com/containerd/containerd/oci"
 | 
						|
	ocispecs "github.com/opencontainers/image-spec/specs-go"
 | 
						|
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 | 
						|
)
 | 
						|
 | 
						|
func (c *Client) exportToOCITar(ctx context.Context, desc ocispec.Descriptor, writer io.Writer, eopts exportOpts) error {
 | 
						|
	tw := tar.NewWriter(writer)
 | 
						|
	img := oci.Tar(tw)
 | 
						|
 | 
						|
	// For tar, we defer creating index until end of the function.
 | 
						|
	if err := oci.Init(img, oci.InitOpts{SkipCreateIndex: true}); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	cs := c.ContentStore()
 | 
						|
	handlers := images.Handlers(
 | 
						|
		images.ChildrenHandler(cs),
 | 
						|
		exportHandler(cs, img),
 | 
						|
	)
 | 
						|
	// For tar, we need to use Walk instead of Dispatch for ensuring sequential write
 | 
						|
	if err := images.Walk(ctx, handlers, desc); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	// For tar, we don't use oci.PutManifestDescriptorToIndex() which allows appending desc to existing index.json
 | 
						|
	// but requires img to support random read access so as to read index.json.
 | 
						|
	return oci.WriteIndex(img,
 | 
						|
		ocispec.Index{
 | 
						|
			Versioned: ocispecs.Versioned{
 | 
						|
				SchemaVersion: 2,
 | 
						|
			},
 | 
						|
			Manifests: []ocispec.Descriptor{desc},
 | 
						|
		},
 | 
						|
	)
 | 
						|
}
 | 
						|
 | 
						|
func exportHandler(cs content.Store, img oci.ImageDriver) images.HandlerFunc {
 | 
						|
	return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
 | 
						|
		r, err := cs.Reader(ctx, desc.Digest)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		w, err := oci.NewBlobWriter(img, desc.Digest.Algorithm())
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		if _, err = io.Copy(w, r); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		if err = w.Commit(desc.Size, desc.Digest); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		if err = w.Close(); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
}
 |