瀏覽代碼

daemon/import: Extract common logic to api

Extract logic that would need to be duplicated in both implementations
of ImageService.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Paweł Gronowski 2 年之前
父節點
當前提交
28327f10a2
共有 4 個文件被更改,包括 83 次插入79 次删除
  1. 2 1
      api/server/router/image/backend.go
  2. 60 2
      api/server/router/image/image_routes.go
  3. 1 1
      daemon/image_service.go
  4. 20 75
      daemon/images/image_import.go

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

@@ -4,6 +4,7 @@ import (
 	"context"
 	"io"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/image"
@@ -31,7 +32,7 @@ type imageBackend interface {
 
 type importExportBackend interface {
 	LoadImage(ctx context.Context, inTar io.ReadCloser, outStream io.Writer, quiet bool) error
-	ImportImage(ctx context.Context, src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
+	ImportImage(ctx context.Context, ref reference.Named, platform *specs.Platform, msg string, layerReader io.Reader, changes []string) (dockerimage.ID, error)
 	ExportImage(ctx context.Context, names []string, outStream io.Writer) error
 }
 

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

@@ -2,7 +2,9 @@ package image // import "github.com/docker/docker/api/server/router/image"
 
 import (
 	"context"
+	"io"
 	"net/http"
+	"net/url"
 	"strconv"
 	"strings"
 	"time"
@@ -15,9 +17,11 @@ import (
 	opts "github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/builder/remotecontext"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/ioutils"
+	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/streamformatter"
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
@@ -33,7 +37,7 @@ func (ir *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrit
 		img         = r.Form.Get("fromImage")
 		repo        = r.Form.Get("repo")
 		tag         = r.Form.Get("tag")
-		message     = r.Form.Get("message")
+		comment     = r.Form.Get("message")
 		progressErr error
 		output      = ioutils.NewWriteFlusher(w)
 		platform    *specs.Platform
@@ -67,7 +71,61 @@ func (ir *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrit
 		progressErr = ir.backend.PullImage(ctx, img, tag, platform, metaHeaders, authConfig, output)
 	} else { // import
 		src := r.Form.Get("fromSrc")
-		progressErr = ir.backend.ImportImage(ctx, src, repo, platform, tag, message, r.Body, output, r.Form["changes"])
+
+		var ref reference.Named
+		if repo != "" {
+			var err error
+			ref, err = reference.ParseNormalizedNamed(repo)
+			if err != nil {
+				return errdefs.InvalidParameter(err)
+			}
+			if _, isCanonical := ref.(reference.Canonical); isCanonical {
+				return errdefs.InvalidParameter(errors.New("cannot import digest reference"))
+			}
+
+			if tag != "" {
+				ref, err = reference.WithTag(ref, tag)
+				if err != nil {
+					return errdefs.InvalidParameter(err)
+				}
+			} else {
+				ref = reference.TagNameOnly(ref)
+			}
+		}
+
+		if len(comment) == 0 {
+			comment = "Imported from " + src
+		}
+
+		var layerReader io.ReadCloser
+		defer r.Body.Close()
+		if src == "-" {
+			layerReader = r.Body
+		} else {
+			if len(strings.Split(src, "://")) == 1 {
+				src = "http://" + src
+			}
+			u, err := url.Parse(src)
+			if err != nil {
+				return errdefs.InvalidParameter(err)
+			}
+
+			resp, err := remotecontext.GetWithStatusError(u.String())
+			if err != nil {
+				return err
+			}
+			output.Write(streamformatter.FormatStatus("", "Downloading from %s", u))
+			progressOutput := streamformatter.NewJSONProgressOutput(output, true)
+			layerReader = progress.NewProgressReader(resp.Body, progressOutput, resp.ContentLength, "", "Importing")
+			defer layerReader.Close()
+		}
+
+		var id image.ID
+		id, progressErr = ir.backend.ImportImage(ctx, ref, platform, comment, layerReader, r.Form["changes"])
+
+		if progressErr == nil {
+			output.Write(streamformatter.FormatStatus("", id.String()))
+		}
 	}
 	if progressErr != nil {
 		if !output.Flushed() {

+ 1 - 1
daemon/image_service.go

@@ -36,7 +36,7 @@ type ImageService interface {
 	LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string)
 	CountImages() int
 	ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error)
-	ImportImage(ctx context.Context, src string, repository string, platform *v1.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
+	ImportImage(ctx context.Context, ref reference.Named, platform *v1.Platform, msg string, layerReader io.Reader, changes []string) (image.ID, error)
 	TagImage(imageName, repository, tag string) (string, error)
 	TagImageWithReference(imageID image.ID, newTag reference.Named) error
 	GetImage(ctx context.Context, refOrID string, options imagetype.GetImageOpts) (*image.Image, error)

+ 20 - 75
daemon/images/image_import.go

@@ -4,102 +4,49 @@ import (
 	"context"
 	"encoding/json"
 	"io"
-	"net/http"
-	"net/url"
-	"strings"
 	"time"
 
 	"github.com/containerd/containerd/platforms"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder/dockerfile"
-	"github.com/docker/docker/builder/remotecontext"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/archive"
-	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/system"
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
-	"github.com/pkg/errors"
 )
 
-// ImportImage imports an image, getting the archived layer data either from
-// inConfig (if src is "-"), or from a URI specified in src. Progress output is
-// written to outStream. Repository and tag names can optionally be given in
-// the repo and tag arguments, respectively.
-func (i *ImageService) ImportImage(ctx context.Context, src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
-	var (
-		rc     io.ReadCloser
-		resp   *http.Response
-		newRef reference.Named
-	)
-
-	if repository != "" {
-		var err error
-		newRef, err = reference.ParseNormalizedNamed(repository)
-		if err != nil {
-			return errdefs.InvalidParameter(err)
-		}
-		if _, isCanonical := newRef.(reference.Canonical); isCanonical {
-			return errdefs.InvalidParameter(errors.New("cannot import digest reference"))
-		}
-
-		if tag != "" {
-			newRef, err = reference.WithTag(newRef, tag)
-			if err != nil {
-				return errdefs.InvalidParameter(err)
-			}
-		}
-	}
-
-	// Normalize platform - default to the operating system and architecture if not supplied.
+// ImportImage imports an image, getting the archived layer data from layerReader.
+// Uncompressed layer archive is passed to the layerStore and handled by the
+// underlying graph driver.
+// Image is tagged with the given reference.
+// If the platform is nil, the default host platform is used.
+// Message is used as the image's history comment.
+// Image configuration is derived from the dockerfile instructions in changes.
+func (i *ImageService) ImportImage(ctx context.Context, newRef reference.Named, platform *specs.Platform, msg string, layerReader io.Reader, changes []string) (image.ID, error) {
 	if platform == nil {
-		p := platforms.DefaultSpec()
-		platform = &p
+		def := platforms.DefaultSpec()
+		platform = &def
 	}
 	if !system.IsOSSupported(platform.OS) {
-		return errdefs.InvalidParameter(system.ErrNotSupportedOperatingSystem)
+		return "", errdefs.InvalidParameter(system.ErrNotSupportedOperatingSystem)
 	}
+
 	config, err := dockerfile.BuildFromConfig(ctx, &container.Config{}, changes, platform.OS)
 	if err != nil {
-		return err
-	}
-	if src == "-" {
-		rc = inConfig
-	} else {
-		inConfig.Close()
-		if len(strings.Split(src, "://")) == 1 {
-			src = "http://" + src
-		}
-		u, err := url.Parse(src)
-		if err != nil {
-			return errdefs.InvalidParameter(err)
-		}
-
-		resp, err = remotecontext.GetWithStatusError(u.String())
-		if err != nil {
-			return err
-		}
-		outStream.Write(streamformatter.FormatStatus("", "Downloading from %s", u))
-		progressOutput := streamformatter.NewJSONProgressOutput(outStream, true)
-		rc = progress.NewProgressReader(resp.Body, progressOutput, resp.ContentLength, "", "Importing")
-	}
-
-	defer rc.Close()
-	if len(msg) == 0 {
-		msg = "Imported from " + src
+		return "", errdefs.InvalidParameter(err)
 	}
 
-	inflatedLayerData, err := archive.DecompressStream(rc)
+	inflatedLayerData, err := archive.DecompressStream(layerReader)
 	if err != nil {
-		return err
+		return "", err
 	}
 	l, err := i.layerStore.Register(inflatedLayerData, "")
 	if err != nil {
-		return err
+		return "", err
 	}
 	defer layer.ReleaseAndLog(i.layerStore, l)
 
@@ -124,22 +71,20 @@ func (i *ImageService) ImportImage(ctx context.Context, src string, repository s
 		}},
 	})
 	if err != nil {
-		return err
+		return "", err
 	}
 
 	id, err := i.imageStore.Create(imgConfig)
 	if err != nil {
-		return err
+		return "", err
 	}
 
-	// FIXME: connect with commit code and call refstore directly
 	if newRef != nil {
 		if err := i.TagImageWithReference(id, newRef); err != nil {
-			return err
+			return "", err
 		}
 	}
 
 	i.LogImageEvent(id.String(), id.String(), "import")
-	outStream.Write(streamformatter.FormatStatus("", id.String()))
-	return nil
+	return id, nil
 }