github.com/olljanat/moby@v1.13.1/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  		sf := streamformatter.NewJSONStreamFormatter()
   122  		output.Write(sf.FormatError(err))
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   129  	metaHeaders := map[string][]string{}
   130  	for k, v := range r.Header {
   131  		if strings.HasPrefix(k, "X-Meta-") {
   132  			metaHeaders[k] = v
   133  		}
   134  	}
   135  	if err := httputils.ParseForm(r); err != nil {
   136  		return err
   137  	}
   138  	authConfig := &types.AuthConfig{}
   139  
   140  	authEncoded := r.Header.Get("X-Registry-Auth")
   141  	if authEncoded != "" {
   142  		// the new format is to handle the authConfig as a header
   143  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   144  		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
   145  			// to increase compatibility to existing api it is defaulting to be empty
   146  			authConfig = &types.AuthConfig{}
   147  		}
   148  	} else {
   149  		// the old format is supported for compatibility if there was no authConfig header
   150  		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
   151  			return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
   152  		}
   153  	}
   154  
   155  	image := vars["name"]
   156  	tag := r.Form.Get("tag")
   157  
   158  	output := ioutils.NewWriteFlusher(w)
   159  	defer output.Close()
   160  
   161  	w.Header().Set("Content-Type", "application/json")
   162  
   163  	if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil {
   164  		if !output.Flushed() {
   165  			return err
   166  		}
   167  		sf := streamformatter.NewJSONStreamFormatter()
   168  		output.Write(sf.FormatError(err))
   169  	}
   170  	return nil
   171  }
   172  
   173  func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   174  	if err := httputils.ParseForm(r); err != nil {
   175  		return err
   176  	}
   177  
   178  	w.Header().Set("Content-Type", "application/x-tar")
   179  
   180  	output := ioutils.NewWriteFlusher(w)
   181  	defer output.Close()
   182  	var names []string
   183  	if name, ok := vars["name"]; ok {
   184  		names = []string{name}
   185  	} else {
   186  		names = r.Form["names"]
   187  	}
   188  
   189  	if err := s.backend.ExportImage(names, output); err != nil {
   190  		if !output.Flushed() {
   191  			return err
   192  		}
   193  		sf := streamformatter.NewJSONStreamFormatter()
   194  		output.Write(sf.FormatError(err))
   195  	}
   196  	return nil
   197  }
   198  
   199  func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   200  	if err := httputils.ParseForm(r); err != nil {
   201  		return err
   202  	}
   203  	quiet := httputils.BoolValueOrDefault(r, "quiet", true)
   204  
   205  	w.Header().Set("Content-Type", "application/json")
   206  
   207  	output := ioutils.NewWriteFlusher(w)
   208  	defer output.Close()
   209  	if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
   210  		output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err))
   211  	}
   212  	return nil
   213  }
   214  
   215  func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   216  	if err := httputils.ParseForm(r); err != nil {
   217  		return err
   218  	}
   219  
   220  	name := vars["name"]
   221  
   222  	if strings.TrimSpace(name) == "" {
   223  		return fmt.Errorf("image name cannot be blank")
   224  	}
   225  
   226  	force := httputils.BoolValue(r, "force")
   227  	prune := !httputils.BoolValue(r, "noprune")
   228  
   229  	list, err := s.backend.ImageDelete(name, force, prune)
   230  	if err != nil {
   231  		return err
   232  	}
   233  
   234  	return httputils.WriteJSON(w, http.StatusOK, list)
   235  }
   236  
   237  func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   238  	imageInspect, err := s.backend.LookupImage(vars["name"])
   239  	if err != nil {
   240  		return err
   241  	}
   242  
   243  	return httputils.WriteJSON(w, http.StatusOK, imageInspect)
   244  }
   245  
   246  func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   247  	if err := httputils.ParseForm(r); err != nil {
   248  		return err
   249  	}
   250  
   251  	imageFilters, err := filters.FromParam(r.Form.Get("filters"))
   252  	if err != nil {
   253  		return err
   254  	}
   255  
   256  	version := httputils.VersionFromContext(ctx)
   257  	filterParam := r.Form.Get("filter")
   258  	if versions.LessThan(version, "1.28") && filterParam != "" {
   259  		imageFilters.Add("reference", filterParam)
   260  	}
   261  
   262  	images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false)
   263  	if err != nil {
   264  		return err
   265  	}
   266  
   267  	return httputils.WriteJSON(w, http.StatusOK, images)
   268  }
   269  
   270  func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   271  	name := vars["name"]
   272  	history, err := s.backend.ImageHistory(name)
   273  	if err != nil {
   274  		return err
   275  	}
   276  
   277  	return httputils.WriteJSON(w, http.StatusOK, history)
   278  }
   279  
   280  func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   281  	if err := httputils.ParseForm(r); err != nil {
   282  		return err
   283  	}
   284  	if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil {
   285  		return err
   286  	}
   287  	w.WriteHeader(http.StatusCreated)
   288  	return nil
   289  }
   290  
   291  func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   292  	if err := httputils.ParseForm(r); err != nil {
   293  		return err
   294  	}
   295  	var (
   296  		config      *types.AuthConfig
   297  		authEncoded = r.Header.Get("X-Registry-Auth")
   298  		headers     = map[string][]string{}
   299  	)
   300  
   301  	if authEncoded != "" {
   302  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   303  		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
   304  			// for a search it is not an error if no auth was given
   305  			// to increase compatibility with the existing api it is defaulting to be empty
   306  			config = &types.AuthConfig{}
   307  		}
   308  	}
   309  	for k, v := range r.Header {
   310  		if strings.HasPrefix(k, "X-Meta-") {
   311  			headers[k] = v
   312  		}
   313  	}
   314  	limit := registry.DefaultSearchLimit
   315  	if r.Form.Get("limit") != "" {
   316  		limitValue, err := strconv.Atoi(r.Form.Get("limit"))
   317  		if err != nil {
   318  			return err
   319  		}
   320  		limit = limitValue
   321  	}
   322  	query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
   323  	if err != nil {
   324  		return err
   325  	}
   326  	return httputils.WriteJSON(w, http.StatusOK, query.Results)
   327  }
   328  
   329  func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   330  	if err := httputils.ParseForm(r); err != nil {
   331  		return err
   332  	}
   333  
   334  	pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
   335  	if err != nil {
   336  		return err
   337  	}
   338  
   339  	pruneReport, err := s.backend.ImagesPrune(pruneFilters)
   340  	if err != nil {
   341  		return err
   342  	}
   343  	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
   344  }