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