api/server/router/image/image_routes.go
dd93571c
 package image
1a775800
 
 import (
 	"encoding/base64"
 	"encoding/json"
 	"io"
 	"net/http"
92f10fe2
 	"strconv"
1a775800
 	"strings"
 
da982cf5
 	"github.com/docker/docker/api/server/httputils"
7c36a1af
 	"github.com/docker/docker/api/types"
93e02efa
 	"github.com/docker/docker/api/types/backend"
7c36a1af
 	"github.com/docker/docker/api/types/container"
820b809e
 	"github.com/docker/docker/api/types/filters"
7c36a1af
 	"github.com/docker/docker/api/types/versions"
1a775800
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/streamformatter"
92f10fe2
 	"github.com/docker/docker/registry"
0380fbff
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
ebcb7d6b
 	"github.com/pkg/errors"
94e3b0f4
 	"golang.org/x/net/context"
1a775800
 )
 
dd93571c
 func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
da982cf5
 	if err := httputils.ParseForm(r); err != nil {
1a775800
 		return err
 	}
 
da982cf5
 	if err := httputils.CheckForJSON(r); err != nil {
1a775800
 		return err
 	}
 
 	cname := r.Form.Get("container")
 
da982cf5
 	pause := httputils.BoolValue(r, "pause")
 	version := httputils.VersionFromContext(ctx)
7534f172
 	if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") {
1a775800
 		pause = true
 	}
 
f0d26e16
 	c, _, _, err := s.decoder.DecodeConfig(r.Body)
1a775800
 	if err != nil && err != io.EOF { //Do not fail if body is empty.
 		return err
 	}
2a2d1f57
 	if c == nil {
7ac4232e
 		c = &container.Config{}
1a775800
 	}
 
93e02efa
 	commitCfg := &backend.ContainerCommitConfig{
 		ContainerCommitConfig: types.ContainerCommitConfig{
 			Pause:        pause,
 			Repo:         r.Form.Get("repo"),
 			Tag:          r.Form.Get("tag"),
 			Author:       r.Form.Get("author"),
 			Comment:      r.Form.Get("comment"),
 			Config:       c,
 			MergeConfigs: true,
 		},
 		Changes: r.Form["changes"],
2a2d1f57
 	}
 
90215065
 	imgID, err := s.backend.Commit(cname, commitCfg)
1a775800
 	if err != nil {
 		return err
 	}
 
2f5f0af3
 	return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{ID: imgID})
1a775800
 }
 
 // Creates an image from Pull or from Import
dd93571c
 func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
da982cf5
 	if err := httputils.ParseForm(r); err != nil {
1a775800
 		return err
 	}
 
 	var (
0380fbff
 		image    = r.Form.Get("fromImage")
 		repo     = r.Form.Get("repo")
 		tag      = r.Form.Get("tag")
 		message  = r.Form.Get("message")
 		err      error
 		output   = ioutils.NewWriteFlusher(w)
 		platform = &specs.Platform{}
1a775800
 	)
ec2289b2
 	defer output.Close()
1a775800
 
 	w.Header().Set("Content-Type", "application/json")
 
0380fbff
 	platform, err = httputils.GetRequestedPlatform(ctx, r)
 	if err == nil {
 		if image != "" { //pull
 			metaHeaders := map[string][]string{}
 			for k, v := range r.Header {
 				if strings.HasPrefix(k, "X-Meta-") {
 					metaHeaders[k] = v
 				}
4352da78
 			}
47afe6bd
 
0380fbff
 			authEncoded := r.Header.Get("X-Registry-Auth")
 			authConfig := &types.AuthConfig{}
 			if authEncoded != "" {
 				authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
 				if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
 					// for a pull it is not an error if no auth was given
 					// to increase compatibility with the existing api it is defaulting to be empty
 					authConfig = &types.AuthConfig{}
 				}
1a775800
 			}
0380fbff
 			err = s.backend.PullImage(ctx, image, tag, platform.OS, metaHeaders, authConfig, output)
 		} else { //import
 			src := r.Form.Get("fromSrc")
 			// 'err' MUST NOT be defined within this block, we need any error
 			// generated from the download to be available to the output
 			// stream processing below
 			err = s.backend.ImportImage(src, repo, platform.OS, tag, message, r.Body, output, r.Form["changes"])
1a775800
 		}
 	}
 	if err != nil {
 		if !output.Flushed() {
 			return err
 		}
c87d67b0
 		output.Write(streamformatter.FormatError(err))
1a775800
 	}
 
 	return nil
 }
 
ebcb7d6b
 type validationError struct {
 	cause error
 }
 
 func (e validationError) Error() string {
 	return e.cause.Error()
 }
 
 func (e validationError) Cause() error {
 	return e.cause
 }
 
 func (validationError) InvalidParameter() {}
 
dd93571c
 func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1a775800
 	metaHeaders := map[string][]string{}
 	for k, v := range r.Header {
 		if strings.HasPrefix(k, "X-Meta-") {
 			metaHeaders[k] = v
 		}
 	}
da982cf5
 	if err := httputils.ParseForm(r); err != nil {
1a775800
 		return err
 	}
5b321e32
 	authConfig := &types.AuthConfig{}
1a775800
 
 	authEncoded := r.Header.Get("X-Registry-Auth")
 	if authEncoded != "" {
 		// the new format is to handle the authConfig as a header
 		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
 		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
 			// to increase compatibility to existing api it is defaulting to be empty
5b321e32
 			authConfig = &types.AuthConfig{}
1a775800
 		}
 	} else {
 		// the old format is supported for compatibility if there was no authConfig header
 		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
ebcb7d6b
 			return errors.Wrap(validationError{err}, "Bad parameters and missing X-Registry-Auth")
1a775800
 		}
 	}
 
47afe6bd
 	image := vars["name"]
4352da78
 	tag := r.Form.Get("tag")
 
1a775800
 	output := ioutils.NewWriteFlusher(w)
ec2289b2
 	defer output.Close()
1a775800
 
 	w.Header().Set("Content-Type", "application/json")
 
47afe6bd
 	if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil {
1a775800
 		if !output.Flushed() {
 			return err
 		}
c87d67b0
 		output.Write(streamformatter.FormatError(err))
1a775800
 	}
 	return nil
 }
 
dd93571c
 func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
da982cf5
 	if err := httputils.ParseForm(r); err != nil {
1a775800
 		return err
 	}
 
 	w.Header().Set("Content-Type", "application/x-tar")
 
 	output := ioutils.NewWriteFlusher(w)
ec2289b2
 	defer output.Close()
1a775800
 	var names []string
 	if name, ok := vars["name"]; ok {
 		names = []string{name}
 	} else {
 		names = r.Form["names"]
 	}
 
90215065
 	if err := s.backend.ExportImage(names, output); err != nil {
1a775800
 		if !output.Flushed() {
 			return err
 		}
c87d67b0
 		output.Write(streamformatter.FormatError(err))
1a775800
 	}
 	return nil
 }
 
06d8f504
 func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
fae09e25
 	if err := httputils.ParseForm(r); err != nil {
 		return err
 	}
 	quiet := httputils.BoolValueOrDefault(r, "quiet", true)
96d7db66
 
2f27632c
 	w.Header().Set("Content-Type", "application/json")
96d7db66
 
2f27632c
 	output := ioutils.NewWriteFlusher(w)
 	defer output.Close()
 	if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
c87d67b0
 		output.Write(streamformatter.FormatError(err))
96d7db66
 	}
2f27632c
 	return nil
1a775800
 }
 
ebcb7d6b
 type missingImageError struct{}
 
 func (missingImageError) Error() string {
 	return "image name cannot be blank"
 }
 
 func (missingImageError) InvalidParameter() {}
 
dd93571c
 func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
da982cf5
 	if err := httputils.ParseForm(r); err != nil {
1a775800
 		return err
 	}
 
 	name := vars["name"]
111d2f34
 
d4a8d09d
 	if strings.TrimSpace(name) == "" {
ebcb7d6b
 		return missingImageError{}
111d2f34
 	}
 
da982cf5
 	force := httputils.BoolValue(r, "force")
 	prune := !httputils.BoolValue(r, "noprune")
1a775800
 
90215065
 	list, err := s.backend.ImageDelete(name, force, prune)
1a775800
 	if err != nil {
 		return err
 	}
 
da982cf5
 	return httputils.WriteJSON(w, http.StatusOK, list)
1a775800
 }
 
dd93571c
 func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
90215065
 	imageInspect, err := s.backend.LookupImage(vars["name"])
1a775800
 	if err != nil {
 		return err
 	}
 
da982cf5
 	return httputils.WriteJSON(w, http.StatusOK, imageInspect)
1a775800
 }
 
dd93571c
 func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
da982cf5
 	if err := httputils.ParseForm(r); err != nil {
1a775800
 		return err
 	}
 
a4efe66c
 	imageFilters, err := filters.FromJSON(r.Form.Get("filters"))
820b809e
 	if err != nil {
 		return err
 	}
 
 	filterParam := r.Form.Get("filter")
0f9d22cd
 	// FIXME(vdemeester) This has been deprecated in 1.13, and is target for removal for v17.12
 	if filterParam != "" {
820b809e
 		imageFilters.Add("reference", filterParam)
 	}
 
 	images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false)
1a775800
 	if err != nil {
 		return err
 	}
 
da982cf5
 	return httputils.WriteJSON(w, http.StatusOK, images)
1a775800
 }
 
dd93571c
 func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1a775800
 	name := vars["name"]
90215065
 	history, err := s.backend.ImageHistory(name)
1a775800
 	if err != nil {
 		return err
 	}
 
da982cf5
 	return httputils.WriteJSON(w, http.StatusOK, history)
1a775800
 }
 
dd93571c
 func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
da982cf5
 	if err := httputils.ParseForm(r); err != nil {
1a775800
 		return err
 	}
47afe6bd
 	if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil {
1a775800
 		return err
 	}
 	w.WriteHeader(http.StatusCreated)
 	return nil
 }
 
dd93571c
 func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
da982cf5
 	if err := httputils.ParseForm(r); err != nil {
1a775800
 		return err
 	}
 	var (
5b321e32
 		config      *types.AuthConfig
1a775800
 		authEncoded = r.Header.Get("X-Registry-Auth")
 		headers     = map[string][]string{}
 	)
 
 	if authEncoded != "" {
 		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
 		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
 			// for a search it is not an error if no auth was given
 			// to increase compatibility with the existing api it is defaulting to be empty
5b321e32
 			config = &types.AuthConfig{}
1a775800
 		}
 	}
 	for k, v := range r.Header {
 		if strings.HasPrefix(k, "X-Meta-") {
 			headers[k] = v
 		}
 	}
92f10fe2
 	limit := registry.DefaultSearchLimit
 	if r.Form.Get("limit") != "" {
 		limitValue, err := strconv.Atoi(r.Form.Get("limit"))
 		if err != nil {
 			return err
 		}
 		limit = limitValue
 	}
 	query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
1a775800
 	if err != nil {
 		return err
 	}
da982cf5
 	return httputils.WriteJSON(w, http.StatusOK, query.Results)
1a775800
 }
33f4d68f
 
 func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := httputils.ParseForm(r); err != nil {
 		return err
 	}
 
a4efe66c
 	pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
a6be56b5
 	if err != nil {
33f4d68f
 		return err
 	}
 
0dee6979
 	pruneReport, err := s.backend.ImagesPrune(ctx, pruneFilters)
33f4d68f
 	if err != nil {
 		return err
 	}
 	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
 }