Quellcode durchsuchen

Merge pull request #43822 from thaJeztah/image_load_save

containerd integration: Implement load/save
Sebastiaan van Stijn vor 2 Jahren
Ursprung
Commit
2279803c8c

+ 2 - 2
api/server/router/image/backend.go

@@ -30,9 +30,9 @@ type imageBackend interface {
 }
 }
 
 
 type importExportBackend interface {
 type importExportBackend interface {
-	LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error
+	LoadImage(ctx context.Context, inTar io.ReadCloser, outStream io.Writer, quiet bool) error
 	ImportImage(src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
 	ImportImage(src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
-	ExportImage(names []string, outStream io.Writer) error
+	ExportImage(ctx context.Context, names []string, outStream io.Writer) error
 }
 }
 
 
 type registryBackend interface {
 type registryBackend interface {

+ 2 - 2
api/server/router/image/image_routes.go

@@ -137,7 +137,7 @@ func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r
 		names = r.Form["names"]
 		names = r.Form["names"]
 	}
 	}
 
 
-	if err := s.backend.ExportImage(names, output); err != nil {
+	if err := s.backend.ExportImage(ctx, names, output); err != nil {
 		if !output.Flushed() {
 		if !output.Flushed() {
 			return err
 			return err
 		}
 		}
@@ -156,7 +156,7 @@ func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter,
 
 
 	output := ioutils.NewWriteFlusher(w)
 	output := ioutils.NewWriteFlusher(w)
 	defer output.Close()
 	defer output.Close()
-	if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
+	if err := s.backend.LoadImage(ctx, r.Body, output, quiet); err != nil {
 		_, _ = output.Write(streamformatter.FormatError(err))
 		_, _ = output.Write(streamformatter.FormatError(err))
 	}
 	}
 	return nil
 	return nil

+ 59 - 5
daemon/containerd/image_exporter.go

@@ -1,19 +1,73 @@
 package containerd
 package containerd
 
 
-import "io"
+import (
+	"context"
+	"io"
+
+	"github.com/containerd/containerd"
+	"github.com/containerd/containerd/images/archive"
+	"github.com/containerd/containerd/platforms"
+	"github.com/docker/distribution/reference"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+)
 
 
 // ExportImage exports a list of images to the given output stream. The
 // ExportImage exports a list of images to the given output stream. The
 // exported images are archived into a tar when written to the output
 // exported images are archived into a tar when written to the output
 // stream. All images with the given tag and all versions containing
 // stream. All images with the given tag and all versions containing
 // the same tag are exported. names is the set of tags to export, and
 // the same tag are exported. names is the set of tags to export, and
 // outStream is the writer which the images are written to.
 // outStream is the writer which the images are written to.
-func (i *ImageService) ExportImage(names []string, outStream io.Writer) error {
-	panic("not implemented")
+//
+// TODO(thaJeztah): produce JSON stream progress response and image events; see https://github.com/moby/moby/issues/43910
+func (i *ImageService) ExportImage(ctx context.Context, names []string, outStream io.Writer) error {
+	opts := []archive.ExportOpt{
+		archive.WithPlatform(platforms.Ordered(platforms.DefaultSpec())),
+		archive.WithSkipNonDistributableBlobs(),
+	}
+	is := i.client.ImageService()
+	for _, imageRef := range names {
+		named, err := reference.ParseDockerRef(imageRef)
+		if err != nil {
+			return err
+		}
+		opts = append(opts, archive.WithImage(is, named.String()))
+	}
+	return i.client.Export(ctx, outStream, opts...)
 }
 }
 
 
 // LoadImage uploads a set of images into the repository. This is the
 // LoadImage uploads a set of images into the repository. This is the
 // complement of ExportImage.  The input stream is an uncompressed tar
 // complement of ExportImage.  The input stream is an uncompressed tar
 // ball containing images and metadata.
 // ball containing images and metadata.
-func (i *ImageService) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
-	panic("not implemented")
+//
+// TODO(thaJeztah): produce JSON stream progress response and image events; see https://github.com/moby/moby/issues/43910
+func (i *ImageService) LoadImage(ctx context.Context, inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
+	platform := platforms.DefaultStrict()
+	imgs, err := i.client.Import(ctx, inTar, containerd.WithImportPlatform(platform))
+
+	if err != nil {
+		// TODO(thaJeztah): remove this log or change to debug once we can; see https://github.com/moby/moby/pull/43822#discussion_r937502405
+		logrus.WithError(err).Warn("failed to import image to containerd")
+		return errors.Wrap(err, "failed to import image")
+	}
+
+	for _, img := range imgs {
+		platformImg := containerd.NewImageWithPlatform(i.client, img, platform)
+
+		unpacked, err := platformImg.IsUnpacked(ctx, containerd.DefaultSnapshotter)
+		if err != nil {
+			// TODO(thaJeztah): remove this log or change to debug once we can; see https://github.com/moby/moby/pull/43822#discussion_r937502405
+			logrus.WithError(err).WithField("image", img.Name).Debug("failed to check if image is unpacked")
+			continue
+		}
+
+		if !unpacked {
+			err := platformImg.Unpack(ctx, containerd.DefaultSnapshotter)
+			if err != nil {
+				// TODO(thaJeztah): remove this log or change to debug once we can; see https://github.com/moby/moby/pull/43822#discussion_r937502405
+				logrus.WithError(err).WithField("image", img.Name).Warn("failed to unpack image")
+				return errors.Wrap(err, "failed to unpack image")
+			}
+		}
+	}
+	return nil
 }
 }

+ 2 - 2
daemon/image_service.go

@@ -29,8 +29,8 @@ type ImageService interface {
 	PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *registry.AuthConfig, outStream io.Writer) error
 	PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *registry.AuthConfig, outStream io.Writer) error
 	CreateImage(config []byte, parent string) (builder.Image, error)
 	CreateImage(config []byte, parent string) (builder.Image, error)
 	ImageDelete(ctx context.Context, imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error)
 	ImageDelete(ctx context.Context, imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error)
-	ExportImage(names []string, outStream io.Writer) error
-	LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error
+	ExportImage(ctx context.Context, names []string, outStream io.Writer) error
+	LoadImage(ctx context.Context, inTar io.ReadCloser, outStream io.Writer, quiet bool) error
 	Images(ctx context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error)
 	Images(ctx context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error)
 	LogImageEvent(imageID, refName, action string)
 	LogImageEvent(imageID, refName, action string)
 	LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string)
 	LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string)

+ 3 - 2
daemon/images/image_exporter.go

@@ -1,6 +1,7 @@
 package images // import "github.com/docker/docker/daemon/images"
 package images // import "github.com/docker/docker/daemon/images"
 
 
 import (
 import (
+	"context"
 	"io"
 	"io"
 
 
 	"github.com/docker/docker/image/tarexport"
 	"github.com/docker/docker/image/tarexport"
@@ -11,7 +12,7 @@ import (
 // stream. All images with the given tag and all versions containing
 // stream. All images with the given tag and all versions containing
 // the same tag are exported. names is the set of tags to export, and
 // the same tag are exported. names is the set of tags to export, and
 // outStream is the writer which the images are written to.
 // outStream is the writer which the images are written to.
-func (i *ImageService) ExportImage(names []string, outStream io.Writer) error {
+func (i *ImageService) ExportImage(ctx context.Context, names []string, outStream io.Writer) error {
 	imageExporter := tarexport.NewTarExporter(i.imageStore, i.layerStore, i.referenceStore, i)
 	imageExporter := tarexport.NewTarExporter(i.imageStore, i.layerStore, i.referenceStore, i)
 	return imageExporter.Save(names, outStream)
 	return imageExporter.Save(names, outStream)
 }
 }
@@ -19,7 +20,7 @@ func (i *ImageService) ExportImage(names []string, outStream io.Writer) error {
 // LoadImage uploads a set of images into the repository. This is the
 // LoadImage uploads a set of images into the repository. This is the
 // complement of ExportImage.  The input stream is an uncompressed tar
 // complement of ExportImage.  The input stream is an uncompressed tar
 // ball containing images and metadata.
 // ball containing images and metadata.
-func (i *ImageService) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
+func (i *ImageService) LoadImage(ctx context.Context, inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
 	imageExporter := tarexport.NewTarExporter(i.imageStore, i.layerStore, i.referenceStore, i)
 	imageExporter := tarexport.NewTarExporter(i.imageStore, i.layerStore, i.referenceStore, i)
 	return imageExporter.Load(inTar, outStream, quiet)
 	return imageExporter.Load(inTar, outStream, quiet)
 }
 }