Skip to content

Commit a62be32

Browse files
committed
Unify docker and oci importer
Signed-off-by: Derek McGowan <[email protected]>
1 parent bce20b7 commit a62be32

7 files changed

Lines changed: 64 additions & 203 deletions

File tree

cmd/ctr/commands/images/import.go

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,11 @@ import (
2020
"fmt"
2121
"io"
2222
"os"
23-
"strings"
2423
"time"
2524

2625
"github.com/containerd/containerd"
2726
"github.com/containerd/containerd/cmd/ctr/commands"
28-
"github.com/containerd/containerd/images/docker"
29-
oci "github.com/containerd/containerd/images/oci"
27+
"github.com/containerd/containerd/images/archive"
3028
"github.com/containerd/containerd/log"
3129
"github.com/urfave/cli"
3230
)
@@ -42,30 +40,25 @@ Implemented formats:
4240
- docker.v1.2
4341
4442
45-
For OCI v1, you may need to specify --base-name because an OCI archive
46-
contains only partial image references (tags without the base image name).
47-
If no base image name is provided, a name will be generated as "import-%{date}".
43+
For OCI v1, you may need to specify --base-name because an OCI archive may
44+
contain only partial image references (tags without the base image name).
45+
If no base image name is provided, a name will be generated as "import-%{yyyy-MM-dd}".
4846
4947
e.g.
50-
$ ctr images import --format oci.v1 --oci-name foo/bar foobar.tar
48+
$ ctr images import --base-name foo/bar foobar.tar
5149
5250
If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadbeef", the command will create
5351
"foo/bar:latest" and "foo/bar@sha256:deadbeef" images in the containerd store.
5452
`,
5553
Flags: append([]cli.Flag{
5654
cli.StringFlag{
57-
Name: "format",
55+
Name: "base-name",
5856
Value: "",
59-
Usage: "image format, by default supports OCI v1, Docker v1.1, Docker v1.2",
60-
},
61-
cli.StringFlag{
62-
Name: "base-name,oci-name",
63-
Value: "",
64-
Usage: "base image name for added images, when provided images without this name prefix are filtered out",
57+
Usage: "base image name for added images, when provided only images with this name prefix are imported",
6558
},
6659
cli.BoolFlag{
6760
Name: "digests",
68-
Usage: "whether to create digest images",
61+
Usage: "whether to create digest images (default: false)",
6962
},
7063
cli.StringFlag{
7164
Name: "index-name",
@@ -82,25 +75,14 @@ If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadb
8275
prefix := context.String("base-name")
8376
if prefix == "" {
8477
prefix = fmt.Sprintf("import-%s", time.Now().Format("2006-01-02"))
85-
opts = append(opts, containerd.WithImageRefTranslator(docker.RefTranslator(prefix, false)))
78+
opts = append(opts, containerd.WithImageRefTranslator(archive.AddRefPrefix(prefix)))
8679
} else {
8780
// When provided, filter out references which do not match
88-
opts = append(opts, containerd.WithImageRefTranslator(docker.RefTranslator(prefix, true)))
89-
}
90-
91-
switch format := context.String("format"); format {
92-
case "", "docker", "docker.v1.1", "docker.v1.2":
93-
opts = append(opts, containerd.WithImporter(&docker.V1Importer{
94-
SkipOCI: strings.HasPrefix(format, "docker"),
95-
}))
96-
case "oci", "oci.v1":
97-
opts = append(opts, containerd.WithImporter(&oci.V1Importer{}))
98-
default:
99-
return fmt.Errorf("unknown format %s", format)
81+
opts = append(opts, containerd.WithImageRefTranslator(archive.FilterRefPrefix(prefix)))
10082
}
10183

10284
if context.Bool("digests") {
103-
opts = append(opts, containerd.WithDigestRef(oci.DigestTranslator(prefix)))
85+
opts = append(opts, containerd.WithDigestRef(archive.DigestTranslator(prefix)))
10486
}
10587

10688
if idxName := context.String("index-name"); idxName != "" {

content/helpers.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, desc o
7070
cw, err := OpenWriter(ctx, cs, WithRef(ref), WithDescriptor(desc))
7171
if err != nil {
7272
if !errdefs.IsAlreadyExists(err) {
73-
return err
73+
return errors.Wrap(err, "failed to open writer")
7474
}
7575

7676
return nil // all ready present
@@ -127,7 +127,7 @@ func OpenWriter(ctx context.Context, cs Ingester, opts ...WriterOpt) (Writer, er
127127
func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected digest.Digest, opts ...Opt) error {
128128
ws, err := cw.Status()
129129
if err != nil {
130-
return err
130+
return errors.Wrap(err, "failed to get status")
131131
}
132132

133133
if ws.Offset > 0 {
@@ -138,7 +138,7 @@ func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected dige
138138
}
139139

140140
if _, err := copyWithBuffer(cw, r); err != nil {
141-
return err
141+
return errors.Wrap(err, "failed to copy")
142142
}
143143

144144
if err := cw.Commit(ctx, size, expected, opts...); err != nil {
Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414
limitations under the License.
1515
*/
1616

17-
// Package docker provides a Docker compatible importer capable of
18-
// importing both Docker and OCI formats.
19-
package docker
17+
// Package archive provides a Docker and OCI compatible importer
18+
package archive
2019

2120
import (
2221
"archive/tar"
@@ -37,19 +36,15 @@ import (
3736
"github.com/pkg/errors"
3837
)
3938

40-
// V1Importer implements Docker v1.1, v1.2 and OCI v1.
41-
type V1Importer struct {
42-
// SkipOCI prevent interpretting OCI files
43-
SkipOCI bool
44-
45-
// TODO: Add option to compress layers on ingest
46-
47-
}
48-
49-
var _ images.Importer = &V1Importer{}
50-
51-
// Import implements Importer.
52-
func (oi *V1Importer) Import(ctx context.Context, store content.Store, reader io.Reader) (ocispec.Descriptor, error) {
39+
// ImportIndex imports an index from a tar achive image bundle
40+
// - implements Docker v1.1, v1.2 and OCI v1.
41+
// - prefers OCI v1 when provided
42+
// - creates OCI index for Docker formats
43+
// - normalizes Docker references and adds as OCI ref name
44+
// e.g. alpine:latest -> docker.io/library/alpine:latest
45+
// - existing OCI reference names are untouched
46+
// - TODO: support option to compress layers on ingest
47+
func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (ocispec.Descriptor, error) {
5348
var (
5449
tr = tar.NewReader(reader)
5550

@@ -82,7 +77,7 @@ func (oi *V1Importer) Import(ctx context.Context, store content.Store, reader io
8277
}
8378

8479
hdrName := path.Clean(hdr.Name)
85-
if hdrName == ocispec.ImageLayoutFile && !oi.SkipOCI {
80+
if hdrName == ocispec.ImageLayoutFile {
8681
if err = onUntarJSON(tr, &ociLayout); err != nil {
8782
return ocispec.Descriptor{}, errors.Wrapf(err, "untar oci layout %q", hdr.Name)
8883
}
@@ -103,6 +98,9 @@ func (oi *V1Importer) Import(ctx context.Context, store content.Store, reader io
10398
}
10499
}
105100

101+
// If OCI layout was given, interpret the tar as an OCI layout.
102+
// When not provided, the layout of the tar will be interpretted
103+
// as Docker v1.1 or v1.2.
106104
if ociLayout.Version != "" {
107105
if ociLayout.Version != ocispec.ImageLayoutVersion {
108106
return ocispec.Descriptor{}, errors.Errorf("unsupported OCI version %s", ociLayout.Version)
@@ -156,7 +154,9 @@ func (oi *V1Importer) Import(ctx context.Context, store content.Store, reader io
156154
return ocispec.Descriptor{}, errors.Wrap(err, "unable to resolve platform")
157155
}
158156
if len(platforms) > 0 {
159-
// Only one platform can be resolved from non-index manifest
157+
// Only one platform can be resolved from non-index manifest,
158+
// The platform can only come from the config included above,
159+
// if the config has no platform it can be safely ommitted.
160160
desc.Platform = &platforms[0]
161161
}
162162

@@ -165,18 +165,18 @@ func (oi *V1Importer) Import(ctx context.Context, store content.Store, reader io
165165
} else {
166166
// Add descriptor per tag
167167
for _, ref := range mfst.RepoTags {
168-
msftdesc := desc
168+
mfstdesc := desc
169169

170170
normalized, err := normalizeReference(ref)
171171
if err != nil {
172172
return ocispec.Descriptor{}, err
173173
}
174174

175-
msftdesc.Annotations = map[string]string{
175+
mfstdesc.Annotations = map[string]string{
176176
ocispec.AnnotationRefName: normalized,
177177
}
178178

179-
idx.Manifests = append(idx.Manifests, msftdesc)
179+
idx.Manifests = append(idx.Manifests, mfstdesc)
180180
}
181181
}
182182
}
Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,31 @@
1414
limitations under the License.
1515
*/
1616

17-
package docker
17+
package archive
1818

1919
import (
2020
"strings"
2121

2222
"github.com/containerd/cri/pkg/util"
23+
digest "github.com/opencontainers/go-digest"
2324
"github.com/pkg/errors"
2425
)
2526

26-
// RefTranslator creates a reference which only has a tag or verifies
27+
// FilterRefPrefix restricts references to having the given image
28+
// prefix. Tag-only references will have the prefix prepended.
29+
func FilterRefPrefix(image string) func(string) string {
30+
return refTranslator(image, true)
31+
}
32+
33+
// AddRefPrefix prepends the given image prefix to tag-only references,
34+
// while leaving returning full references unmodified.
35+
func AddRefPrefix(image string) func(string) string {
36+
return refTranslator(image, false)
37+
}
38+
39+
// refTranslator creates a reference which only has a tag or verifies
2740
// a full reference.
28-
func RefTranslator(image string, checkPrefix bool) func(string) string {
41+
func refTranslator(image string, checkPrefix bool) func(string) string {
2942
return func(ref string) string {
3043
// Check if ref is full reference
3144
if strings.ContainsAny(ref, "/:@") {
@@ -63,3 +76,11 @@ func normalizeReference(ref string) (string, error) {
6376

6477
return normalized.String(), nil
6578
}
79+
80+
// DigestTranslator creates a digest reference by adding the
81+
// digest to an image name
82+
func DigestTranslator(prefix string) func(digest.Digest) string {
83+
return func(dgst digest.Digest) string {
84+
return prefix + "@" + dgst.String()
85+
}
86+
}

images/oci/importer.go

Lines changed: 0 additions & 127 deletions
This file was deleted.

0 commit comments

Comments
 (0)