github.com/devdivbcp/moby@v17.12.0-ce-rc1.0.20200726071732-2d4bfdc789ad+incompatible/api/server/router/image/image_routes.go (about)

     1  package image // import "github.com/docker/docker/api/server/router/image"
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"net/http"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/containerd/containerd/platforms"
    12  	"github.com/docker/docker/api/server/httputils"
    13  	"github.com/docker/docker/api/types"
    14  	"github.com/docker/docker/api/types/filters"
    15  	"github.com/docker/docker/api/types/versions"
    16  	"github.com/docker/docker/errdefs"
    17  	"github.com/docker/docker/pkg/ioutils"
    18  	"github.com/docker/docker/pkg/streamformatter"
    19  	"github.com/docker/docker/pkg/system"
    20  	"github.com/docker/docker/registry"
    21  	specs "github.com/opencontainers/image-spec/specs-go/v1"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  // Creates an image from Pull or from Import
    26  func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    27  
    28  	if err := httputils.ParseForm(r); err != nil {
    29  		return err
    30  	}
    31  
    32  	var (
    33  		image    = r.Form.Get("fromImage")
    34  		repo     = r.Form.Get("repo")
    35  		tag      = r.Form.Get("tag")
    36  		message  = r.Form.Get("message")
    37  		err      error
    38  		output   = ioutils.NewWriteFlusher(w)
    39  		platform *specs.Platform
    40  	)
    41  	defer output.Close()
    42  
    43  	w.Header().Set("Content-Type", "application/json")
    44  
    45  	version := httputils.VersionFromContext(ctx)
    46  	if versions.GreaterThanOrEqualTo(version, "1.32") {
    47  		apiPlatform := r.FormValue("platform")
    48  		if apiPlatform != "" {
    49  			sp, err := platforms.Parse(apiPlatform)
    50  			if err != nil {
    51  				return err
    52  			}
    53  			if err := system.ValidatePlatform(sp); err != nil {
    54  				return err
    55  			}
    56  			platform = &sp
    57  		}
    58  	}
    59  
    60  	if image != "" { //pull
    61  		metaHeaders := map[string][]string{}
    62  		for k, v := range r.Header {
    63  			if strings.HasPrefix(k, "X-Meta-") {
    64  				metaHeaders[k] = v
    65  			}
    66  		}
    67  
    68  		authEncoded := r.Header.Get("X-Registry-Auth")
    69  		authConfig := &types.AuthConfig{}
    70  		if authEncoded != "" {
    71  			authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
    72  			if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
    73  				// for a pull it is not an error if no auth was given
    74  				// to increase compatibility with the existing api it is defaulting to be empty
    75  				authConfig = &types.AuthConfig{}
    76  			}
    77  		}
    78  		err = s.backend.PullImage(ctx, image, tag, platform, metaHeaders, authConfig, output)
    79  	} else { //import
    80  		src := r.Form.Get("fromSrc")
    81  		// 'err' MUST NOT be defined within this block, we need any error
    82  		// generated from the download to be available to the output
    83  		// stream processing below
    84  		os := ""
    85  		if platform != nil {
    86  			os = platform.OS
    87  		}
    88  		err = s.backend.ImportImage(src, repo, os, tag, message, r.Body, output, r.Form["changes"])
    89  	}
    90  	if err != nil {
    91  		if !output.Flushed() {
    92  			return err
    93  		}
    94  		output.Write(streamformatter.FormatError(err))
    95  	}
    96  
    97  	return nil
    98  }
    99  
   100  func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   101  	metaHeaders := map[string][]string{}
   102  	for k, v := range r.Header {
   103  		if strings.HasPrefix(k, "X-Meta-") {
   104  			metaHeaders[k] = v
   105  		}
   106  	}
   107  	if err := httputils.ParseForm(r); err != nil {
   108  		return err
   109  	}
   110  	authConfig := &types.AuthConfig{}
   111  
   112  	authEncoded := r.Header.Get("X-Registry-Auth")
   113  	if authEncoded != "" {
   114  		// the new format is to handle the authConfig as a header
   115  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   116  		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
   117  			// to increase compatibility to existing api it is defaulting to be empty
   118  			authConfig = &types.AuthConfig{}
   119  		}
   120  	} else {
   121  		// the old format is supported for compatibility if there was no authConfig header
   122  		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
   123  			return errors.Wrap(errdefs.InvalidParameter(err), "Bad parameters and missing X-Registry-Auth")
   124  		}
   125  	}
   126  
   127  	image := vars["name"]
   128  	tag := r.Form.Get("tag")
   129  
   130  	output := ioutils.NewWriteFlusher(w)
   131  	defer output.Close()
   132  
   133  	w.Header().Set("Content-Type", "application/json")
   134  
   135  	if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil {
   136  		if !output.Flushed() {
   137  			return err
   138  		}
   139  		output.Write(streamformatter.FormatError(err))
   140  	}
   141  	return nil
   142  }
   143  
   144  func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   145  	if err := httputils.ParseForm(r); err != nil {
   146  		return err
   147  	}
   148  
   149  	w.Header().Set("Content-Type", "application/x-tar")
   150  
   151  	output := ioutils.NewWriteFlusher(w)
   152  	defer output.Close()
   153  	var names []string
   154  	if name, ok := vars["name"]; ok {
   155  		names = []string{name}
   156  	} else {
   157  		names = r.Form["names"]
   158  	}
   159  
   160  	if err := s.backend.ExportImage(names, output); err != nil {
   161  		if !output.Flushed() {
   162  			return err
   163  		}
   164  		output.Write(streamformatter.FormatError(err))
   165  	}
   166  	return nil
   167  }
   168  
   169  func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   170  	if err := httputils.ParseForm(r); err != nil {
   171  		return err
   172  	}
   173  	quiet := httputils.BoolValueOrDefault(r, "quiet", true)
   174  
   175  	w.Header().Set("Content-Type", "application/json")
   176  
   177  	output := ioutils.NewWriteFlusher(w)
   178  	defer output.Close()
   179  	if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
   180  		output.Write(streamformatter.FormatError(err))
   181  	}
   182  	return nil
   183  }
   184  
   185  type missingImageError struct{}
   186  
   187  func (missingImageError) Error() string {
   188  	return "image name cannot be blank"
   189  }
   190  
   191  func (missingImageError) InvalidParameter() {}
   192  
   193  func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   194  	if err := httputils.ParseForm(r); err != nil {
   195  		return err
   196  	}
   197  
   198  	name := vars["name"]
   199  
   200  	if strings.TrimSpace(name) == "" {
   201  		return missingImageError{}
   202  	}
   203  
   204  	force := httputils.BoolValue(r, "force")
   205  	prune := !httputils.BoolValue(r, "noprune")
   206  
   207  	list, err := s.backend.ImageDelete(name, force, prune)
   208  	if err != nil {
   209  		return err
   210  	}
   211  
   212  	return httputils.WriteJSON(w, http.StatusOK, list)
   213  }
   214  
   215  func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   216  	imageInspect, err := s.backend.LookupImage(vars["name"])
   217  	if err != nil {
   218  		return err
   219  	}
   220  
   221  	return httputils.WriteJSON(w, http.StatusOK, imageInspect)
   222  }
   223  
   224  func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   225  	if err := httputils.ParseForm(r); err != nil {
   226  		return err
   227  	}
   228  
   229  	imageFilters, err := filters.FromJSON(r.Form.Get("filters"))
   230  	if err != nil {
   231  		return err
   232  	}
   233  
   234  	filterParam := r.Form.Get("filter")
   235  	// FIXME(vdemeester) This has been deprecated in 1.13, and is target for removal for v17.12
   236  	if filterParam != "" {
   237  		imageFilters.Add("reference", filterParam)
   238  	}
   239  
   240  	images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false)
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	return httputils.WriteJSON(w, http.StatusOK, images)
   246  }
   247  
   248  func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   249  	name := vars["name"]
   250  	history, err := s.backend.ImageHistory(name)
   251  	if err != nil {
   252  		return err
   253  	}
   254  
   255  	return httputils.WriteJSON(w, http.StatusOK, history)
   256  }
   257  
   258  func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   259  	if err := httputils.ParseForm(r); err != nil {
   260  		return err
   261  	}
   262  	if _, err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil {
   263  		return err
   264  	}
   265  	w.WriteHeader(http.StatusCreated)
   266  	return nil
   267  }
   268  
   269  func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   270  	if err := httputils.ParseForm(r); err != nil {
   271  		return err
   272  	}
   273  	var (
   274  		config      *types.AuthConfig
   275  		authEncoded = r.Header.Get("X-Registry-Auth")
   276  		headers     = map[string][]string{}
   277  	)
   278  
   279  	if authEncoded != "" {
   280  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   281  		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
   282  			// for a search it is not an error if no auth was given
   283  			// to increase compatibility with the existing api it is defaulting to be empty
   284  			config = &types.AuthConfig{}
   285  		}
   286  	}
   287  	for k, v := range r.Header {
   288  		if strings.HasPrefix(k, "X-Meta-") {
   289  			headers[k] = v
   290  		}
   291  	}
   292  	limit := registry.DefaultSearchLimit
   293  	if r.Form.Get("limit") != "" {
   294  		limitValue, err := strconv.Atoi(r.Form.Get("limit"))
   295  		if err != nil {
   296  			return err
   297  		}
   298  		limit = limitValue
   299  	}
   300  	query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
   301  	if err != nil {
   302  		return err
   303  	}
   304  	return httputils.WriteJSON(w, http.StatusOK, query.Results)
   305  }
   306  
   307  func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   308  	if err := httputils.ParseForm(r); err != nil {
   309  		return err
   310  	}
   311  
   312  	pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
   313  	if err != nil {
   314  		return err
   315  	}
   316  
   317  	pruneReport, err := s.backend.ImagesPrune(ctx, pruneFilters)
   318  	if err != nil {
   319  		return err
   320  	}
   321  	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
   322  }