github.com/shishir-a412ed/docker@v1.3.2-0.20180103180333-fda904911d87/api/server/router/image/image_routes.go (about)

     1  package image
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/docker/docker/api/server/httputils"
    14  	"github.com/docker/docker/api/types"
    15  	"github.com/docker/docker/api/types/backend"
    16  	"github.com/docker/docker/api/types/filters"
    17  	"github.com/docker/docker/api/types/versions"
    18  	"github.com/docker/docker/pkg/ioutils"
    19  	"github.com/docker/docker/pkg/streamformatter"
    20  	"github.com/docker/docker/pkg/system"
    21  	"github.com/docker/docker/registry"
    22  	specs "github.com/opencontainers/image-spec/specs-go/v1"
    23  	"github.com/pkg/errors"
    24  	"golang.org/x/net/context"
    25  )
    26  
    27  func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    28  	if err := httputils.ParseForm(r); err != nil {
    29  		return err
    30  	}
    31  
    32  	if err := httputils.CheckForJSON(r); err != nil {
    33  		return err
    34  	}
    35  
    36  	cname := r.Form.Get("container")
    37  
    38  	pause := httputils.BoolValue(r, "pause")
    39  	version := httputils.VersionFromContext(ctx)
    40  	if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") {
    41  		pause = true
    42  	}
    43  
    44  	c, _, _, err := s.decoder.DecodeConfig(r.Body)
    45  	if err != nil && err != io.EOF { //Do not fail if body is empty.
    46  		return err
    47  	}
    48  
    49  	commitCfg := &backend.ContainerCommitConfig{
    50  		ContainerCommitConfig: types.ContainerCommitConfig{
    51  			Pause:        pause,
    52  			Repo:         r.Form.Get("repo"),
    53  			Tag:          r.Form.Get("tag"),
    54  			Author:       r.Form.Get("author"),
    55  			Comment:      r.Form.Get("comment"),
    56  			Config:       c,
    57  			MergeConfigs: true,
    58  		},
    59  		Changes: r.Form["changes"],
    60  	}
    61  
    62  	imgID, err := s.backend.Commit(cname, commitCfg)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{ID: imgID})
    68  }
    69  
    70  // Creates an image from Pull or from Import
    71  func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    72  
    73  	if err := httputils.ParseForm(r); err != nil {
    74  		return err
    75  	}
    76  
    77  	var (
    78  		image    = r.Form.Get("fromImage")
    79  		repo     = r.Form.Get("repo")
    80  		tag      = r.Form.Get("tag")
    81  		message  = r.Form.Get("message")
    82  		err      error
    83  		output   = ioutils.NewWriteFlusher(w)
    84  		platform = &specs.Platform{}
    85  	)
    86  	defer output.Close()
    87  
    88  	w.Header().Set("Content-Type", "application/json")
    89  
    90  	version := httputils.VersionFromContext(ctx)
    91  	if versions.GreaterThanOrEqualTo(version, "1.32") {
    92  		// TODO @jhowardmsft. The following environment variable is an interim
    93  		// measure to allow the daemon to have a default platform if omitted by
    94  		// the client. This allows LCOW and WCOW to work with a down-level CLI
    95  		// for a short period of time, as the CLI changes can't be merged
    96  		// until after the daemon changes have been merged. Once the CLI is
    97  		// updated, this can be removed. PR for CLI is currently in
    98  		// https://github.com/docker/cli/pull/474.
    99  		apiPlatform := r.FormValue("platform")
   100  		if system.LCOWSupported() && apiPlatform == "" {
   101  			apiPlatform = os.Getenv("LCOW_API_PLATFORM_IF_OMITTED")
   102  		}
   103  		platform = system.ParsePlatform(apiPlatform)
   104  		if err = system.ValidatePlatform(platform); err != nil {
   105  			err = fmt.Errorf("invalid platform: %s", err)
   106  		}
   107  	}
   108  
   109  	if err == nil {
   110  		if image != "" { //pull
   111  			metaHeaders := map[string][]string{}
   112  			for k, v := range r.Header {
   113  				if strings.HasPrefix(k, "X-Meta-") {
   114  					metaHeaders[k] = v
   115  				}
   116  			}
   117  
   118  			authEncoded := r.Header.Get("X-Registry-Auth")
   119  			authConfig := &types.AuthConfig{}
   120  			if authEncoded != "" {
   121  				authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   122  				if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
   123  					// for a pull it is not an error if no auth was given
   124  					// to increase compatibility with the existing api it is defaulting to be empty
   125  					authConfig = &types.AuthConfig{}
   126  				}
   127  			}
   128  			err = s.backend.PullImage(ctx, image, tag, platform.OS, metaHeaders, authConfig, output)
   129  		} else { //import
   130  			src := r.Form.Get("fromSrc")
   131  			// 'err' MUST NOT be defined within this block, we need any error
   132  			// generated from the download to be available to the output
   133  			// stream processing below
   134  			err = s.backend.ImportImage(src, repo, platform.OS, tag, message, r.Body, output, r.Form["changes"])
   135  		}
   136  	}
   137  	if err != nil {
   138  		if !output.Flushed() {
   139  			return err
   140  		}
   141  		output.Write(streamformatter.FormatError(err))
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  type validationError struct {
   148  	cause error
   149  }
   150  
   151  func (e validationError) Error() string {
   152  	return e.cause.Error()
   153  }
   154  
   155  func (e validationError) Cause() error {
   156  	return e.cause
   157  }
   158  
   159  func (validationError) InvalidParameter() {}
   160  
   161  func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   162  	metaHeaders := map[string][]string{}
   163  	for k, v := range r.Header {
   164  		if strings.HasPrefix(k, "X-Meta-") {
   165  			metaHeaders[k] = v
   166  		}
   167  	}
   168  	if err := httputils.ParseForm(r); err != nil {
   169  		return err
   170  	}
   171  	authConfig := &types.AuthConfig{}
   172  
   173  	authEncoded := r.Header.Get("X-Registry-Auth")
   174  	if authEncoded != "" {
   175  		// the new format is to handle the authConfig as a header
   176  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   177  		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
   178  			// to increase compatibility to existing api it is defaulting to be empty
   179  			authConfig = &types.AuthConfig{}
   180  		}
   181  	} else {
   182  		// the old format is supported for compatibility if there was no authConfig header
   183  		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
   184  			return errors.Wrap(validationError{err}, "Bad parameters and missing X-Registry-Auth")
   185  		}
   186  	}
   187  
   188  	image := vars["name"]
   189  	tag := r.Form.Get("tag")
   190  
   191  	output := ioutils.NewWriteFlusher(w)
   192  	defer output.Close()
   193  
   194  	w.Header().Set("Content-Type", "application/json")
   195  
   196  	if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil {
   197  		if !output.Flushed() {
   198  			return err
   199  		}
   200  		output.Write(streamformatter.FormatError(err))
   201  	}
   202  	return nil
   203  }
   204  
   205  func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   206  	if err := httputils.ParseForm(r); err != nil {
   207  		return err
   208  	}
   209  
   210  	w.Header().Set("Content-Type", "application/x-tar")
   211  
   212  	output := ioutils.NewWriteFlusher(w)
   213  	defer output.Close()
   214  	var names []string
   215  	if name, ok := vars["name"]; ok {
   216  		names = []string{name}
   217  	} else {
   218  		names = r.Form["names"]
   219  	}
   220  
   221  	if err := s.backend.ExportImage(names, output); err != nil {
   222  		if !output.Flushed() {
   223  			return err
   224  		}
   225  		output.Write(streamformatter.FormatError(err))
   226  	}
   227  	return nil
   228  }
   229  
   230  func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   231  	if err := httputils.ParseForm(r); err != nil {
   232  		return err
   233  	}
   234  	quiet := httputils.BoolValueOrDefault(r, "quiet", true)
   235  
   236  	w.Header().Set("Content-Type", "application/json")
   237  
   238  	output := ioutils.NewWriteFlusher(w)
   239  	defer output.Close()
   240  	if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
   241  		output.Write(streamformatter.FormatError(err))
   242  	}
   243  	return nil
   244  }
   245  
   246  type missingImageError struct{}
   247  
   248  func (missingImageError) Error() string {
   249  	return "image name cannot be blank"
   250  }
   251  
   252  func (missingImageError) InvalidParameter() {}
   253  
   254  func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   255  	if err := httputils.ParseForm(r); err != nil {
   256  		return err
   257  	}
   258  
   259  	name := vars["name"]
   260  
   261  	if strings.TrimSpace(name) == "" {
   262  		return missingImageError{}
   263  	}
   264  
   265  	force := httputils.BoolValue(r, "force")
   266  	prune := !httputils.BoolValue(r, "noprune")
   267  
   268  	list, err := s.backend.ImageDelete(name, force, prune)
   269  	if err != nil {
   270  		return err
   271  	}
   272  
   273  	return httputils.WriteJSON(w, http.StatusOK, list)
   274  }
   275  
   276  func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   277  	imageInspect, err := s.backend.LookupImage(vars["name"])
   278  	if err != nil {
   279  		return err
   280  	}
   281  
   282  	return httputils.WriteJSON(w, http.StatusOK, imageInspect)
   283  }
   284  
   285  func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   286  	if err := httputils.ParseForm(r); err != nil {
   287  		return err
   288  	}
   289  
   290  	imageFilters, err := filters.FromJSON(r.Form.Get("filters"))
   291  	if err != nil {
   292  		return err
   293  	}
   294  
   295  	filterParam := r.Form.Get("filter")
   296  	// FIXME(vdemeester) This has been deprecated in 1.13, and is target for removal for v17.12
   297  	if filterParam != "" {
   298  		imageFilters.Add("reference", filterParam)
   299  	}
   300  
   301  	images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false)
   302  	if err != nil {
   303  		return err
   304  	}
   305  
   306  	return httputils.WriteJSON(w, http.StatusOK, images)
   307  }
   308  
   309  func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   310  	name := vars["name"]
   311  	history, err := s.backend.ImageHistory(name)
   312  	if err != nil {
   313  		return err
   314  	}
   315  
   316  	return httputils.WriteJSON(w, http.StatusOK, history)
   317  }
   318  
   319  func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   320  	if err := httputils.ParseForm(r); err != nil {
   321  		return err
   322  	}
   323  	if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil {
   324  		return err
   325  	}
   326  	w.WriteHeader(http.StatusCreated)
   327  	return nil
   328  }
   329  
   330  func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   331  	if err := httputils.ParseForm(r); err != nil {
   332  		return err
   333  	}
   334  	var (
   335  		config      *types.AuthConfig
   336  		authEncoded = r.Header.Get("X-Registry-Auth")
   337  		headers     = map[string][]string{}
   338  	)
   339  
   340  	if authEncoded != "" {
   341  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   342  		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
   343  			// for a search it is not an error if no auth was given
   344  			// to increase compatibility with the existing api it is defaulting to be empty
   345  			config = &types.AuthConfig{}
   346  		}
   347  	}
   348  	for k, v := range r.Header {
   349  		if strings.HasPrefix(k, "X-Meta-") {
   350  			headers[k] = v
   351  		}
   352  	}
   353  	limit := registry.DefaultSearchLimit
   354  	if r.Form.Get("limit") != "" {
   355  		limitValue, err := strconv.Atoi(r.Form.Get("limit"))
   356  		if err != nil {
   357  			return err
   358  		}
   359  		limit = limitValue
   360  	}
   361  	query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
   362  	if err != nil {
   363  		return err
   364  	}
   365  	return httputils.WriteJSON(w, http.StatusOK, query.Results)
   366  }
   367  
   368  func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   369  	if err := httputils.ParseForm(r); err != nil {
   370  		return err
   371  	}
   372  
   373  	pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
   374  	if err != nil {
   375  		return err
   376  	}
   377  
   378  	pruneReport, err := s.backend.ImagesPrune(ctx, pruneFilters)
   379  	if err != nil {
   380  		return err
   381  	}
   382  	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
   383  }