github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/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  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/docker/docker/api/server/httputils"
    13  	"github.com/docker/docker/api/types"
    14  	"github.com/docker/docker/api/types/backend"
    15  	"github.com/docker/docker/api/types/container"
    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/registry"
    21  	"golang.org/x/net/context"
    22  )
    23  
    24  func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    25  	if err := httputils.ParseForm(r); err != nil {
    26  		return err
    27  	}
    28  
    29  	if err := httputils.CheckForJSON(r); err != nil {
    30  		return err
    31  	}
    32  
    33  	cname := r.Form.Get("container")
    34  
    35  	pause := httputils.BoolValue(r, "pause")
    36  	version := httputils.VersionFromContext(ctx)
    37  	if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") {
    38  		pause = true
    39  	}
    40  
    41  	c, _, _, err := s.decoder.DecodeConfig(r.Body)
    42  	if err != nil && err != io.EOF { //Do not fail if body is empty.
    43  		return err
    44  	}
    45  	if c == nil {
    46  		c = &container.Config{}
    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{
    68  		ID: string(imgID),
    69  	})
    70  }
    71  
    72  // Creates an image from Pull or from Import
    73  func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    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  	)
    86  	defer output.Close()
    87  
    88  	w.Header().Set("Content-Type", "application/json")
    89  
    90  	if image != "" { //pull
    91  		metaHeaders := map[string][]string{}
    92  		for k, v := range r.Header {
    93  			if strings.HasPrefix(k, "X-Meta-") {
    94  				metaHeaders[k] = v
    95  			}
    96  		}
    97  
    98  		authEncoded := r.Header.Get("X-Registry-Auth")
    99  		authConfig := &types.AuthConfig{}
   100  		if authEncoded != "" {
   101  			authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   102  			if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
   103  				// for a pull it is not an error if no auth was given
   104  				// to increase compatibility with the existing api it is defaulting to be empty
   105  				authConfig = &types.AuthConfig{}
   106  			}
   107  		}
   108  
   109  		err = s.backend.PullImage(ctx, image, tag, metaHeaders, authConfig, output)
   110  	} else { //import
   111  		src := r.Form.Get("fromSrc")
   112  		// 'err' MUST NOT be defined within this block, we need any error
   113  		// generated from the download to be available to the output
   114  		// stream processing below
   115  		err = s.backend.ImportImage(src, repo, tag, message, r.Body, output, r.Form["changes"])
   116  	}
   117  	if err != nil {
   118  		if !output.Flushed() {
   119  			return err
   120  		}
   121  		output.Write(streamformatter.FormatError(err))
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   128  	metaHeaders := map[string][]string{}
   129  	for k, v := range r.Header {
   130  		if strings.HasPrefix(k, "X-Meta-") {
   131  			metaHeaders[k] = v
   132  		}
   133  	}
   134  	if err := httputils.ParseForm(r); err != nil {
   135  		return err
   136  	}
   137  	authConfig := &types.AuthConfig{}
   138  
   139  	authEncoded := r.Header.Get("X-Registry-Auth")
   140  	if authEncoded != "" {
   141  		// the new format is to handle the authConfig as a header
   142  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   143  		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
   144  			// to increase compatibility to existing api it is defaulting to be empty
   145  			authConfig = &types.AuthConfig{}
   146  		}
   147  	} else {
   148  		// the old format is supported for compatibility if there was no authConfig header
   149  		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
   150  			return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
   151  		}
   152  	}
   153  
   154  	image := vars["name"]
   155  	tag := r.Form.Get("tag")
   156  
   157  	output := ioutils.NewWriteFlusher(w)
   158  	defer output.Close()
   159  
   160  	w.Header().Set("Content-Type", "application/json")
   161  
   162  	if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, 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) getImagesGet(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  
   176  	w.Header().Set("Content-Type", "application/x-tar")
   177  
   178  	output := ioutils.NewWriteFlusher(w)
   179  	defer output.Close()
   180  	var names []string
   181  	if name, ok := vars["name"]; ok {
   182  		names = []string{name}
   183  	} else {
   184  		names = r.Form["names"]
   185  	}
   186  
   187  	if err := s.backend.ExportImage(names, output); err != nil {
   188  		if !output.Flushed() {
   189  			return err
   190  		}
   191  		output.Write(streamformatter.FormatError(err))
   192  	}
   193  	return nil
   194  }
   195  
   196  func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   197  	if err := httputils.ParseForm(r); err != nil {
   198  		return err
   199  	}
   200  	quiet := httputils.BoolValueOrDefault(r, "quiet", true)
   201  
   202  	w.Header().Set("Content-Type", "application/json")
   203  
   204  	output := ioutils.NewWriteFlusher(w)
   205  	defer output.Close()
   206  	if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
   207  		output.Write(streamformatter.FormatError(err))
   208  	}
   209  	return nil
   210  }
   211  
   212  func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   213  	if err := httputils.ParseForm(r); err != nil {
   214  		return err
   215  	}
   216  
   217  	name := vars["name"]
   218  
   219  	if strings.TrimSpace(name) == "" {
   220  		return fmt.Errorf("image name cannot be blank")
   221  	}
   222  
   223  	force := httputils.BoolValue(r, "force")
   224  	prune := !httputils.BoolValue(r, "noprune")
   225  
   226  	list, err := s.backend.ImageDelete(name, force, prune)
   227  	if err != nil {
   228  		return err
   229  	}
   230  
   231  	return httputils.WriteJSON(w, http.StatusOK, list)
   232  }
   233  
   234  func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   235  	imageInspect, err := s.backend.LookupImage(vars["name"])
   236  	if err != nil {
   237  		return err
   238  	}
   239  
   240  	return httputils.WriteJSON(w, http.StatusOK, imageInspect)
   241  }
   242  
   243  func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   244  	if err := httputils.ParseForm(r); err != nil {
   245  		return err
   246  	}
   247  
   248  	imageFilters, err := filters.FromParam(r.Form.Get("filters"))
   249  	if err != nil {
   250  		return err
   251  	}
   252  
   253  	filterParam := r.Form.Get("filter")
   254  	// FIXME(vdemeester) This has been deprecated in 1.13, and is target for removal for v17.12
   255  	if filterParam != "" {
   256  		imageFilters.Add("reference", filterParam)
   257  	}
   258  
   259  	images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false)
   260  	if err != nil {
   261  		return err
   262  	}
   263  
   264  	return httputils.WriteJSON(w, http.StatusOK, images)
   265  }
   266  
   267  func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   268  	name := vars["name"]
   269  	history, err := s.backend.ImageHistory(name)
   270  	if err != nil {
   271  		return err
   272  	}
   273  
   274  	return httputils.WriteJSON(w, http.StatusOK, history)
   275  }
   276  
   277  func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   278  	if err := httputils.ParseForm(r); err != nil {
   279  		return err
   280  	}
   281  	if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil {
   282  		return err
   283  	}
   284  	w.WriteHeader(http.StatusCreated)
   285  	return nil
   286  }
   287  
   288  func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   289  	if err := httputils.ParseForm(r); err != nil {
   290  		return err
   291  	}
   292  	var (
   293  		config      *types.AuthConfig
   294  		authEncoded = r.Header.Get("X-Registry-Auth")
   295  		headers     = map[string][]string{}
   296  	)
   297  
   298  	if authEncoded != "" {
   299  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   300  		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
   301  			// for a search it is not an error if no auth was given
   302  			// to increase compatibility with the existing api it is defaulting to be empty
   303  			config = &types.AuthConfig{}
   304  		}
   305  	}
   306  	for k, v := range r.Header {
   307  		if strings.HasPrefix(k, "X-Meta-") {
   308  			headers[k] = v
   309  		}
   310  	}
   311  	limit := registry.DefaultSearchLimit
   312  	if r.Form.Get("limit") != "" {
   313  		limitValue, err := strconv.Atoi(r.Form.Get("limit"))
   314  		if err != nil {
   315  			return err
   316  		}
   317  		limit = limitValue
   318  	}
   319  	query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
   320  	if err != nil {
   321  		return err
   322  	}
   323  	return httputils.WriteJSON(w, http.StatusOK, query.Results)
   324  }
   325  
   326  func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   327  	if err := httputils.ParseForm(r); err != nil {
   328  		return err
   329  	}
   330  
   331  	pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
   332  	if err != nil {
   333  		return err
   334  	}
   335  
   336  	pruneReport, err := s.backend.ImagesPrune(ctx, pruneFilters)
   337  	if err != nil {
   338  		return err
   339  	}
   340  	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
   341  }