 b518f11dba
			
		
	
	b518f11dba
	
	
	
		
			
			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>
		
	
		
			
				
	
	
		
			120 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			120 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package containerd
 | |
| 
 | |
| import (
 | |
| 	"archive/tar"
 | |
| 	"context"
 | |
| 	"encoding/json"
 | |
| 	"io"
 | |
| 	"io/ioutil"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/containerd/containerd/content"
 | |
| 	"github.com/containerd/containerd/errdefs"
 | |
| 	"github.com/containerd/containerd/images"
 | |
| 	"github.com/containerd/containerd/reference"
 | |
| 	digest "github.com/opencontainers/go-digest"
 | |
| 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 | |
| 	"github.com/pkg/errors"
 | |
| )
 | |
| 
 | |
| func resolveOCIIndex(idx ocispec.Index, refObject string) (*ocispec.Descriptor, error) {
 | |
| 	tag, dgst := reference.SplitObject(refObject)
 | |
| 	if tag == "" && dgst == "" {
 | |
| 		return nil, errors.Errorf("unexpected object: %q", refObject)
 | |
| 	}
 | |
| 	for _, m := range idx.Manifests {
 | |
| 		if m.Digest == dgst {
 | |
| 			return &m, nil
 | |
| 		}
 | |
| 		annot, ok := m.Annotations[ocispec.AnnotationRefName]
 | |
| 		if ok && annot == tag && tag != "" {
 | |
| 			return &m, nil
 | |
| 		}
 | |
| 	}
 | |
| 	return nil, errors.Errorf("not found: %q", refObject)
 | |
| }
 | |
| 
 | |
| func (c *Client) importFromOCITar(ctx context.Context, ref string, reader io.Reader, iopts importOpts) (Image, error) {
 | |
| 	tr := tar.NewReader(reader)
 | |
| 	store := c.ContentStore()
 | |
| 	var desc *ocispec.Descriptor
 | |
| 	for {
 | |
| 		hdr, err := tr.Next()
 | |
| 		if err == io.EOF {
 | |
| 			break
 | |
| 		}
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if hdr.Typeflag != tar.TypeReg && hdr.Typeflag != tar.TypeRegA {
 | |
| 			continue
 | |
| 		}
 | |
| 		if hdr.Name == "index.json" {
 | |
| 			desc, err = onUntarIndexJSON(tr, iopts.refObject)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			continue
 | |
| 		}
 | |
| 		if strings.HasPrefix(hdr.Name, "blobs/") {
 | |
| 			if err := onUntarBlob(ctx, tr, store, hdr.Name, hdr.Size); err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if desc == nil {
 | |
| 		return nil, errors.Errorf("no descriptor found for reference object %q", iopts.refObject)
 | |
| 	}
 | |
| 	imgrec := images.Image{
 | |
| 		Name:   ref,
 | |
| 		Target: *desc,
 | |
| 	}
 | |
| 	is := c.ImageService()
 | |
| 	if updated, err := is.Update(ctx, imgrec, "target"); err != nil {
 | |
| 		if !errdefs.IsNotFound(err) {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		created, err := is.Create(ctx, imgrec)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		imgrec = created
 | |
| 	} else {
 | |
| 		imgrec = updated
 | |
| 	}
 | |
| 
 | |
| 	img := &image{
 | |
| 		client: c,
 | |
| 		i:      imgrec,
 | |
| 	}
 | |
| 	return img, nil
 | |
| }
 | |
| 
 | |
| func onUntarIndexJSON(r io.Reader, refObject string) (*ocispec.Descriptor, error) {
 | |
| 	b, err := ioutil.ReadAll(r)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	var idx ocispec.Index
 | |
| 	if err := json.Unmarshal(b, &idx); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return resolveOCIIndex(idx, refObject)
 | |
| }
 | |
| 
 | |
| func onUntarBlob(ctx context.Context, r io.Reader, store content.Store, name string, size int64) error {
 | |
| 	// name is like "blobs/sha256/deadbeef"
 | |
| 	split := strings.Split(name, "/")
 | |
| 	if len(split) != 3 {
 | |
| 		return errors.Errorf("unexpected name: %q", name)
 | |
| 	}
 | |
| 	algo := digest.Algorithm(split[1])
 | |
| 	if !algo.Available() {
 | |
| 		return errors.Errorf("unsupported algorithm: %s", algo)
 | |
| 	}
 | |
| 	dgst := digest.NewDigestFromHex(algo.String(), split[2])
 | |
| 	return content.WriteBlob(ctx, store, "unknown-"+dgst.String(), r, size, dgst)
 | |
| }
 |