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