Merge pull request #1880 from AkihiroSuda/refactor-importer
importer: refactor and fix GC
This commit is contained in:
159
client.go
159
client.go
@@ -30,7 +30,6 @@ import (
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/reference"
|
||||
"github.com/containerd/containerd/remotes"
|
||||
"github.com/containerd/containerd/remotes/docker"
|
||||
"github.com/containerd/containerd/remotes/docker/schema1"
|
||||
@@ -503,95 +502,27 @@ func (c *Client) Version(ctx context.Context) (Version, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
type imageFormat string
|
||||
|
||||
const (
|
||||
ociImageFormat imageFormat = "oci"
|
||||
)
|
||||
|
||||
type importOpts struct {
|
||||
format imageFormat
|
||||
refObject string
|
||||
labels map[string]string
|
||||
}
|
||||
|
||||
// ImportOpt allows the caller to specify import specific options
|
||||
type ImportOpt func(c *importOpts) error
|
||||
|
||||
// WithImportLabel sets a label to be associated with an imported image
|
||||
func WithImportLabel(key, value string) ImportOpt {
|
||||
return func(opts *importOpts) error {
|
||||
if opts.labels == nil {
|
||||
opts.labels = make(map[string]string)
|
||||
}
|
||||
|
||||
opts.labels[key] = value
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithImportLabels associates a set of labels to an imported image
|
||||
func WithImportLabels(labels map[string]string) ImportOpt {
|
||||
return func(opts *importOpts) error {
|
||||
if opts.labels == nil {
|
||||
opts.labels = make(map[string]string)
|
||||
}
|
||||
|
||||
for k, v := range labels {
|
||||
opts.labels[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithOCIImportFormat sets the import format for an OCI image format
|
||||
func WithOCIImportFormat() ImportOpt {
|
||||
return func(c *importOpts) error {
|
||||
if c.format != "" {
|
||||
return errors.New("format already set")
|
||||
}
|
||||
c.format = ociImageFormat
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithRefObject specifies the ref object to import.
|
||||
// If refObject is empty, it is copied from the ref argument of Import().
|
||||
func WithRefObject(refObject string) ImportOpt {
|
||||
return func(c *importOpts) error {
|
||||
c.refObject = refObject
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func resolveImportOpt(ref string, opts ...ImportOpt) (importOpts, error) {
|
||||
func resolveImportOpt(opts ...ImportOpt) (importOpts, error) {
|
||||
var iopts importOpts
|
||||
for _, o := range opts {
|
||||
if err := o(&iopts); err != nil {
|
||||
return iopts, err
|
||||
}
|
||||
}
|
||||
// use OCI as the default format
|
||||
if iopts.format == "" {
|
||||
iopts.format = ociImageFormat
|
||||
}
|
||||
// if refObject is not explicitly specified, use the one specified in ref
|
||||
if iopts.refObject == "" {
|
||||
refSpec, err := reference.Parse(ref)
|
||||
if err != nil {
|
||||
return iopts, err
|
||||
}
|
||||
iopts.refObject = refSpec.Object
|
||||
}
|
||||
return iopts, nil
|
||||
}
|
||||
|
||||
// Import imports an image from a Tar stream using reader.
|
||||
// OCI format is assumed by default.
|
||||
//
|
||||
// Note that unreferenced blobs are imported to the content store as well.
|
||||
func (c *Client) Import(ctx context.Context, ref string, reader io.Reader, opts ...ImportOpt) (Image, error) {
|
||||
iopts, err := resolveImportOpt(ref, opts...)
|
||||
// Caller needs to specify importer. Future version may use oci.v1 as the default.
|
||||
// Note that unreferrenced blobs may be imported to the content store as well.
|
||||
func (c *Client) Import(ctx context.Context, importer images.Importer, reader io.Reader, opts ...ImportOpt) ([]Image, error) {
|
||||
_, err := resolveImportOpt(opts...) // unused now
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -602,58 +533,66 @@ func (c *Client) Import(ctx context.Context, ref string, reader io.Reader, opts
|
||||
}
|
||||
defer done()
|
||||
|
||||
switch iopts.format {
|
||||
case ociImageFormat:
|
||||
return c.importFromOCITar(ctx, ref, reader, iopts)
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported format: %s", iopts.format)
|
||||
imgrecs, err := importer.Import(ctx, c.ContentStore(), reader)
|
||||
if err != nil {
|
||||
// is.Update() is not called on error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
is := c.ImageService()
|
||||
var images []Image
|
||||
for _, imgrec := range imgrecs {
|
||||
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
|
||||
}
|
||||
|
||||
images = append(images, &image{
|
||||
client: c,
|
||||
i: imgrec,
|
||||
})
|
||||
}
|
||||
return images, nil
|
||||
}
|
||||
|
||||
type exportOpts struct {
|
||||
format imageFormat
|
||||
}
|
||||
|
||||
// ExportOpt allows callers to set export options
|
||||
// ExportOpt allows the caller to specify export-specific options
|
||||
type ExportOpt func(c *exportOpts) error
|
||||
|
||||
// WithOCIExportFormat sets the OCI image format as the export target
|
||||
func WithOCIExportFormat() ExportOpt {
|
||||
return func(c *exportOpts) error {
|
||||
if c.format != "" {
|
||||
return errors.New("format already set")
|
||||
func resolveExportOpt(opts ...ExportOpt) (exportOpts, error) {
|
||||
var eopts exportOpts
|
||||
for _, o := range opts {
|
||||
if err := o(&eopts); err != nil {
|
||||
return eopts, err
|
||||
}
|
||||
c.format = ociImageFormat
|
||||
return nil
|
||||
}
|
||||
return eopts, nil
|
||||
}
|
||||
|
||||
// TODO: add WithMediaTypeTranslation that transforms media types according to the format.
|
||||
// e.g. application/vnd.docker.image.rootfs.diff.tar.gzip
|
||||
// -> application/vnd.oci.image.layer.v1.tar+gzip
|
||||
|
||||
// Export exports an image to a Tar stream.
|
||||
// OCI format is used by default.
|
||||
// It is up to caller to put "org.opencontainers.image.ref.name" annotation to desc.
|
||||
func (c *Client) Export(ctx context.Context, desc ocispec.Descriptor, opts ...ExportOpt) (io.ReadCloser, error) {
|
||||
var eopts exportOpts
|
||||
for _, o := range opts {
|
||||
if err := o(&eopts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// use OCI as the default format
|
||||
if eopts.format == "" {
|
||||
eopts.format = ociImageFormat
|
||||
// TODO(AkihiroSuda): support exporting multiple descriptors at once to a single archive stream.
|
||||
func (c *Client) Export(ctx context.Context, exporter images.Exporter, desc ocispec.Descriptor, opts ...ExportOpt) (io.ReadCloser, error) {
|
||||
_, err := resolveExportOpt(opts...) // unused now
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
switch eopts.format {
|
||||
case ociImageFormat:
|
||||
go func() {
|
||||
pw.CloseWithError(c.exportToOCITar(ctx, desc, pw, eopts))
|
||||
}()
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported format: %s", eopts.format)
|
||||
}
|
||||
go func() {
|
||||
pw.CloseWithError(exporter.Export(ctx, c.ContentStore(), desc, pw))
|
||||
}()
|
||||
return pr, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user