github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/api/server/router/local/image.go (about)

     1  package local
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"github.com/docker/distribution/digest"
    13  	"github.com/docker/docker/api/server/httputils"
    14  	"github.com/docker/docker/builder/dockerfile"
    15  	derr "github.com/docker/docker/errors"
    16  	"github.com/docker/docker/pkg/ioutils"
    17  	"github.com/docker/docker/pkg/streamformatter"
    18  	"github.com/docker/docker/reference"
    19  	"github.com/docker/docker/runconfig"
    20  	"github.com/docker/engine-api/types"
    21  	"github.com/docker/engine-api/types/container"
    22  	"golang.org/x/net/context"
    23  )
    24  
    25  func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    26  	if err := httputils.ParseForm(r); err != nil {
    27  		return err
    28  	}
    29  
    30  	if err := httputils.CheckForJSON(r); err != nil {
    31  		return err
    32  	}
    33  
    34  	cname := r.Form.Get("container")
    35  
    36  	pause := httputils.BoolValue(r, "pause")
    37  	version := httputils.VersionFromContext(ctx)
    38  	if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
    39  		pause = true
    40  	}
    41  
    42  	c, _, _, err := runconfig.DecodeContainerConfig(r.Body)
    43  	if err != nil && err != io.EOF { //Do not fail if body is empty.
    44  		return err
    45  	}
    46  	if c == nil {
    47  		c = &container.Config{}
    48  	}
    49  
    50  	if !s.daemon.Exists(cname) {
    51  		return derr.ErrorCodeNoSuchContainer.WithArgs(cname)
    52  	}
    53  
    54  	newConfig, err := dockerfile.BuildFromConfig(c, r.Form["changes"])
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	commitCfg := &types.ContainerCommitConfig{
    60  		Pause:        pause,
    61  		Repo:         r.Form.Get("repo"),
    62  		Tag:          r.Form.Get("tag"),
    63  		Author:       r.Form.Get("author"),
    64  		Comment:      r.Form.Get("comment"),
    65  		Config:       newConfig,
    66  		MergeConfigs: true,
    67  	}
    68  
    69  	imgID, err := s.daemon.Commit(cname, commitCfg)
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{
    75  		ID: string(imgID),
    76  	})
    77  }
    78  
    79  // Creates an image from Pull or from Import
    80  func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    81  	if err := httputils.ParseForm(r); err != nil {
    82  		return err
    83  	}
    84  
    85  	var (
    86  		image   = r.Form.Get("fromImage")
    87  		repo    = r.Form.Get("repo")
    88  		tag     = r.Form.Get("tag")
    89  		message = r.Form.Get("message")
    90  	)
    91  	authEncoded := r.Header.Get("X-Registry-Auth")
    92  	authConfig := &types.AuthConfig{}
    93  	if authEncoded != "" {
    94  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
    95  		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
    96  			// for a pull it is not an error if no auth was given
    97  			// to increase compatibility with the existing api it is defaulting to be empty
    98  			authConfig = &types.AuthConfig{}
    99  		}
   100  	}
   101  
   102  	var (
   103  		err    error
   104  		output = ioutils.NewWriteFlusher(w)
   105  	)
   106  	defer output.Close()
   107  
   108  	w.Header().Set("Content-Type", "application/json")
   109  
   110  	if image != "" { //pull
   111  		// Special case: "pull -a" may send an image name with a
   112  		// trailing :. This is ugly, but let's not break API
   113  		// compatibility.
   114  		image = strings.TrimSuffix(image, ":")
   115  
   116  		var ref reference.Named
   117  		ref, err = reference.ParseNamed(image)
   118  		if err == nil {
   119  			if tag != "" {
   120  				// The "tag" could actually be a digest.
   121  				var dgst digest.Digest
   122  				dgst, err = digest.ParseDigest(tag)
   123  				if err == nil {
   124  					ref, err = reference.WithDigest(ref, dgst)
   125  				} else {
   126  					ref, err = reference.WithTag(ref, tag)
   127  				}
   128  			}
   129  			if err == nil {
   130  				metaHeaders := map[string][]string{}
   131  				for k, v := range r.Header {
   132  					if strings.HasPrefix(k, "X-Meta-") {
   133  						metaHeaders[k] = v
   134  					}
   135  				}
   136  
   137  				err = s.daemon.PullImage(ref, metaHeaders, authConfig, output)
   138  			}
   139  		}
   140  	} else { //import
   141  		var newRef reference.Named
   142  		if repo != "" {
   143  			var err error
   144  			newRef, err = reference.ParseNamed(repo)
   145  			if err != nil {
   146  				return err
   147  			}
   148  
   149  			if _, isCanonical := newRef.(reference.Canonical); isCanonical {
   150  				return errors.New("cannot import digest reference")
   151  			}
   152  
   153  			if tag != "" {
   154  				newRef, err = reference.WithTag(newRef, tag)
   155  				if err != nil {
   156  					return err
   157  				}
   158  			}
   159  		}
   160  
   161  		src := r.Form.Get("fromSrc")
   162  
   163  		// 'err' MUST NOT be defined within this block, we need any error
   164  		// generated from the download to be available to the output
   165  		// stream processing below
   166  		var newConfig *container.Config
   167  		newConfig, err = dockerfile.BuildFromConfig(&container.Config{}, r.Form["changes"])
   168  		if err != nil {
   169  			return err
   170  		}
   171  
   172  		err = s.daemon.ImportImage(src, newRef, message, r.Body, output, newConfig)
   173  	}
   174  	if err != nil {
   175  		if !output.Flushed() {
   176  			return err
   177  		}
   178  		sf := streamformatter.NewJSONStreamFormatter()
   179  		output.Write(sf.FormatError(err))
   180  	}
   181  
   182  	return nil
   183  }
   184  
   185  func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   186  	metaHeaders := map[string][]string{}
   187  	for k, v := range r.Header {
   188  		if strings.HasPrefix(k, "X-Meta-") {
   189  			metaHeaders[k] = v
   190  		}
   191  	}
   192  	if err := httputils.ParseForm(r); err != nil {
   193  		return err
   194  	}
   195  	authConfig := &types.AuthConfig{}
   196  
   197  	authEncoded := r.Header.Get("X-Registry-Auth")
   198  	if authEncoded != "" {
   199  		// the new format is to handle the authConfig as a header
   200  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   201  		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
   202  			// to increase compatibility to existing api it is defaulting to be empty
   203  			authConfig = &types.AuthConfig{}
   204  		}
   205  	} else {
   206  		// the old format is supported for compatibility if there was no authConfig header
   207  		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
   208  			return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
   209  		}
   210  	}
   211  
   212  	ref, err := reference.ParseNamed(vars["name"])
   213  	if err != nil {
   214  		return err
   215  	}
   216  	tag := r.Form.Get("tag")
   217  	if tag != "" {
   218  		// Push by digest is not supported, so only tags are supported.
   219  		ref, err = reference.WithTag(ref, tag)
   220  		if err != nil {
   221  			return err
   222  		}
   223  	}
   224  
   225  	output := ioutils.NewWriteFlusher(w)
   226  	defer output.Close()
   227  
   228  	w.Header().Set("Content-Type", "application/json")
   229  
   230  	if err := s.daemon.PushImage(ref, metaHeaders, authConfig, output); err != nil {
   231  		if !output.Flushed() {
   232  			return err
   233  		}
   234  		sf := streamformatter.NewJSONStreamFormatter()
   235  		output.Write(sf.FormatError(err))
   236  	}
   237  	return nil
   238  }
   239  
   240  func (s *router) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   241  	if err := httputils.ParseForm(r); err != nil {
   242  		return err
   243  	}
   244  
   245  	w.Header().Set("Content-Type", "application/x-tar")
   246  
   247  	output := ioutils.NewWriteFlusher(w)
   248  	defer output.Close()
   249  	var names []string
   250  	if name, ok := vars["name"]; ok {
   251  		names = []string{name}
   252  	} else {
   253  		names = r.Form["names"]
   254  	}
   255  
   256  	if err := s.daemon.ExportImage(names, output); err != nil {
   257  		if !output.Flushed() {
   258  			return err
   259  		}
   260  		sf := streamformatter.NewJSONStreamFormatter()
   261  		output.Write(sf.FormatError(err))
   262  	}
   263  	return nil
   264  }
   265  
   266  func (s *router) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   267  	return s.daemon.LoadImage(r.Body, w)
   268  }
   269  
   270  func (s *router) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   271  	if err := httputils.ParseForm(r); err != nil {
   272  		return err
   273  	}
   274  
   275  	name := vars["name"]
   276  
   277  	if strings.TrimSpace(name) == "" {
   278  		return fmt.Errorf("image name cannot be blank")
   279  	}
   280  
   281  	force := httputils.BoolValue(r, "force")
   282  	prune := !httputils.BoolValue(r, "noprune")
   283  
   284  	list, err := s.daemon.ImageDelete(name, force, prune)
   285  	if err != nil {
   286  		return err
   287  	}
   288  
   289  	return httputils.WriteJSON(w, http.StatusOK, list)
   290  }
   291  
   292  func (s *router) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   293  	imageInspect, err := s.daemon.LookupImage(vars["name"])
   294  	if err != nil {
   295  		return err
   296  	}
   297  
   298  	return httputils.WriteJSON(w, http.StatusOK, imageInspect)
   299  }
   300  
   301  func (s *router) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   302  	if err := httputils.ParseForm(r); err != nil {
   303  		return err
   304  	}
   305  
   306  	// FIXME: The filter parameter could just be a match filter
   307  	images, err := s.daemon.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
   308  	if err != nil {
   309  		return err
   310  	}
   311  
   312  	return httputils.WriteJSON(w, http.StatusOK, images)
   313  }
   314  
   315  func (s *router) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   316  	name := vars["name"]
   317  	history, err := s.daemon.ImageHistory(name)
   318  	if err != nil {
   319  		return err
   320  	}
   321  
   322  	return httputils.WriteJSON(w, http.StatusOK, history)
   323  }
   324  
   325  func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   326  	if err := httputils.ParseForm(r); err != nil {
   327  		return err
   328  	}
   329  	repo := r.Form.Get("repo")
   330  	tag := r.Form.Get("tag")
   331  	newTag, err := reference.WithName(repo)
   332  	if err != nil {
   333  		return err
   334  	}
   335  	if tag != "" {
   336  		if newTag, err = reference.WithTag(newTag, tag); err != nil {
   337  			return err
   338  		}
   339  	}
   340  	if err := s.daemon.TagImage(newTag, vars["name"]); err != nil {
   341  		return err
   342  	}
   343  	w.WriteHeader(http.StatusCreated)
   344  	return nil
   345  }
   346  
   347  func (s *router) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   348  	if err := httputils.ParseForm(r); err != nil {
   349  		return err
   350  	}
   351  	var (
   352  		config      *types.AuthConfig
   353  		authEncoded = r.Header.Get("X-Registry-Auth")
   354  		headers     = map[string][]string{}
   355  	)
   356  
   357  	if authEncoded != "" {
   358  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   359  		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
   360  			// for a search it is not an error if no auth was given
   361  			// to increase compatibility with the existing api it is defaulting to be empty
   362  			config = &types.AuthConfig{}
   363  		}
   364  	}
   365  	for k, v := range r.Header {
   366  		if strings.HasPrefix(k, "X-Meta-") {
   367  			headers[k] = v
   368  		}
   369  	}
   370  	query, err := s.daemon.SearchRegistryForImages(r.Form.Get("term"), config, headers)
   371  	if err != nil {
   372  		return err
   373  	}
   374  	return httputils.WriteJSON(w, http.StatusOK, query.Results)
   375  }