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) }