github.com/portworx/docker@v1.12.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/backend"
    14  	"github.com/docker/docker/pkg/ioutils"
    15  	"github.com/docker/docker/pkg/streamformatter"
    16  	"github.com/docker/docker/registry"
    17  	"github.com/docker/engine-api/types"
    18  	"github.com/docker/engine-api/types/container"
    19  	"github.com/docker/engine-api/types/versions"
    20  	"golang.org/x/net/context"
    21  )
    22  
    23  func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    24  	if err := httputils.ParseForm(r); err != nil {
    25  		return err
    26  	}
    27  
    28  	if err := httputils.CheckForJSON(r); err != nil {
    29  		return err
    30  	}
    31  
    32  	cname := r.Form.Get("container")
    33  
    34  	pause := httputils.BoolValue(r, "pause")
    35  	version := httputils.VersionFromContext(ctx)
    36  	if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") {
    37  		pause = true
    38  	}
    39  
    40  	c, _, _, err := s.decoder.DecodeConfig(r.Body)
    41  	if err != nil && err != io.EOF { //Do not fail if body is empty.
    42  		return err
    43  	}
    44  	if c == nil {
    45  		c = &container.Config{}
    46  	}
    47  
    48  	commitCfg := &backend.ContainerCommitConfig{
    49  		ContainerCommitConfig: types.ContainerCommitConfig{
    50  			Pause:        pause,
    51  			Repo:         r.Form.Get("repo"),
    52  			Tag:          r.Form.Get("tag"),
    53  			Author:       r.Form.Get("author"),
    54  			Comment:      r.Form.Get("comment"),
    55  			Config:       c,
    56  			MergeConfigs: true,
    57  		},
    58  		Changes: r.Form["changes"],
    59  	}
    60  
    61  	imgID, err := s.backend.Commit(cname, commitCfg)
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{
    67  		ID: string(imgID),
    68  	})
    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  	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  	)
    85  	defer output.Close()
    86  
    87  	w.Header().Set("Content-Type", "application/json")
    88  
    89  	if image != "" { //pull
    90  		metaHeaders := map[string][]string{}
    91  		for k, v := range r.Header {
    92  			if strings.HasPrefix(k, "X-Meta-") {
    93  				metaHeaders[k] = v
    94  			}
    95  		}
    96  
    97  		authEncoded := r.Header.Get("X-Registry-Auth")
    98  		authConfig := &types.AuthConfig{}
    99  		if authEncoded != "" {
   100  			authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   101  			if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
   102  				// for a pull it is not an error if no auth was given
   103  				// to increase compatibility with the existing api it is defaulting to be empty
   104  				authConfig = &types.AuthConfig{}
   105  			}
   106  		}
   107  
   108  		err = s.backend.PullImage(ctx, image, tag, metaHeaders, authConfig, output)
   109  	} else { //import
   110  		src := r.Form.Get("fromSrc")
   111  		// 'err' MUST NOT be defined within this block, we need any error
   112  		// generated from the download to be available to the output
   113  		// stream processing below
   114  		err = s.backend.ImportImage(src, repo, tag, message, r.Body, output, r.Form["changes"])
   115  	}
   116  	if err != nil {
   117  		if !output.Flushed() {
   118  			return err
   119  		}
   120  		sf := streamformatter.NewJSONStreamFormatter()
   121  		output.Write(sf.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  		sf := streamformatter.NewJSONStreamFormatter()
   167  		output.Write(sf.FormatError(err))
   168  	}
   169  	return nil
   170  }
   171  
   172  func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   173  	if err := httputils.ParseForm(r); err != nil {
   174  		return err
   175  	}
   176  
   177  	w.Header().Set("Content-Type", "application/x-tar")
   178  
   179  	output := ioutils.NewWriteFlusher(w)
   180  	defer output.Close()
   181  	var names []string
   182  	if name, ok := vars["name"]; ok {
   183  		names = []string{name}
   184  	} else {
   185  		names = r.Form["names"]
   186  	}
   187  
   188  	if err := s.backend.ExportImage(names, output); err != nil {
   189  		if !output.Flushed() {
   190  			return err
   191  		}
   192  		sf := streamformatter.NewJSONStreamFormatter()
   193  		output.Write(sf.FormatError(err))
   194  	}
   195  	return nil
   196  }
   197  
   198  func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   199  	if err := httputils.ParseForm(r); err != nil {
   200  		return err
   201  	}
   202  	quiet := httputils.BoolValueOrDefault(r, "quiet", true)
   203  
   204  	if !quiet {
   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  	return s.backend.LoadImage(r.Body, w, quiet)
   215  }
   216  
   217  func (s *imageRouter) deleteImages(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  
   222  	name := vars["name"]
   223  
   224  	if strings.TrimSpace(name) == "" {
   225  		return fmt.Errorf("image name cannot be blank")
   226  	}
   227  
   228  	force := httputils.BoolValue(r, "force")
   229  	prune := !httputils.BoolValue(r, "noprune")
   230  
   231  	list, err := s.backend.ImageDelete(name, force, prune)
   232  	if err != nil {
   233  		return err
   234  	}
   235  
   236  	return httputils.WriteJSON(w, http.StatusOK, list)
   237  }
   238  
   239  func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   240  	imageInspect, err := s.backend.LookupImage(vars["name"])
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	return httputils.WriteJSON(w, http.StatusOK, imageInspect)
   246  }
   247  
   248  func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   249  	if err := httputils.ParseForm(r); err != nil {
   250  		return err
   251  	}
   252  
   253  	// FIXME: The filter parameter could just be a match filter
   254  	images, err := s.backend.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
   255  	if err != nil {
   256  		return err
   257  	}
   258  
   259  	return httputils.WriteJSON(w, http.StatusOK, images)
   260  }
   261  
   262  func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   263  	name := vars["name"]
   264  	history, err := s.backend.ImageHistory(name)
   265  	if err != nil {
   266  		return err
   267  	}
   268  
   269  	return httputils.WriteJSON(w, http.StatusOK, history)
   270  }
   271  
   272  func (s *imageRouter) postImagesTag(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  	if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil {
   277  		return err
   278  	}
   279  	w.WriteHeader(http.StatusCreated)
   280  	return nil
   281  }
   282  
   283  func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   284  	if err := httputils.ParseForm(r); err != nil {
   285  		return err
   286  	}
   287  	var (
   288  		config      *types.AuthConfig
   289  		authEncoded = r.Header.Get("X-Registry-Auth")
   290  		headers     = map[string][]string{}
   291  	)
   292  
   293  	if authEncoded != "" {
   294  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   295  		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
   296  			// for a search it is not an error if no auth was given
   297  			// to increase compatibility with the existing api it is defaulting to be empty
   298  			config = &types.AuthConfig{}
   299  		}
   300  	}
   301  	for k, v := range r.Header {
   302  		if strings.HasPrefix(k, "X-Meta-") {
   303  			headers[k] = v
   304  		}
   305  	}
   306  	limit := registry.DefaultSearchLimit
   307  	if r.Form.Get("limit") != "" {
   308  		limitValue, err := strconv.Atoi(r.Form.Get("limit"))
   309  		if err != nil {
   310  			return err
   311  		}
   312  		limit = limitValue
   313  	}
   314  	query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
   315  	if err != nil {
   316  		return err
   317  	}
   318  	return httputils.WriteJSON(w, http.StatusOK, query.Results)
   319  }