github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/api/server/image.go (about)

     1  package server
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  	"github.com/docker/docker/api/types"
    14  	"github.com/docker/docker/builder"
    15  	"github.com/docker/docker/cliconfig"
    16  	"github.com/docker/docker/context"
    17  	"github.com/docker/docker/graph"
    18  	"github.com/docker/docker/pkg/ioutils"
    19  	"github.com/docker/docker/pkg/parsers"
    20  	"github.com/docker/docker/pkg/streamformatter"
    21  	"github.com/docker/docker/pkg/ulimit"
    22  	"github.com/docker/docker/runconfig"
    23  	"github.com/docker/docker/utils"
    24  )
    25  
    26  func (s *Server) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    27  	if err := parseForm(r); err != nil {
    28  		return err
    29  	}
    30  
    31  	if err := checkForJSON(r); err != nil {
    32  		return err
    33  	}
    34  
    35  	cname := r.Form.Get("container")
    36  
    37  	pause := boolValue(r, "pause")
    38  	version := ctx.Version()
    39  	if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
    40  		pause = true
    41  	}
    42  
    43  	c, _, err := runconfig.DecodeContainerConfig(r.Body)
    44  	if err != nil && err != io.EOF { //Do not fail if body is empty.
    45  		return err
    46  	}
    47  
    48  	commitCfg := &builder.CommitConfig{
    49  		Pause:   pause,
    50  		Repo:    r.Form.Get("repo"),
    51  		Tag:     r.Form.Get("tag"),
    52  		Author:  r.Form.Get("author"),
    53  		Comment: r.Form.Get("comment"),
    54  		Changes: r.Form["changes"],
    55  		Config:  c,
    56  	}
    57  
    58  	imgID, err := builder.Commit(cname, s.daemon, commitCfg)
    59  	if err != nil {
    60  		return err
    61  	}
    62  
    63  	return writeJSON(w, http.StatusCreated, &types.ContainerCommitResponse{
    64  		ID: imgID,
    65  	})
    66  }
    67  
    68  // Creates an image from Pull or from Import
    69  func (s *Server) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    70  	if err := parseForm(r); err != nil {
    71  		return err
    72  	}
    73  
    74  	var (
    75  		image   = r.Form.Get("fromImage")
    76  		repo    = r.Form.Get("repo")
    77  		tag     = r.Form.Get("tag")
    78  		message = r.Form.Get("message")
    79  	)
    80  	authEncoded := r.Header.Get("X-Registry-Auth")
    81  	authConfig := &cliconfig.AuthConfig{}
    82  	if authEncoded != "" {
    83  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
    84  		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
    85  			// for a pull it is not an error if no auth was given
    86  			// to increase compatibility with the existing api it is defaulting to be empty
    87  			authConfig = &cliconfig.AuthConfig{}
    88  		}
    89  	}
    90  
    91  	var (
    92  		err    error
    93  		output = ioutils.NewWriteFlusher(w)
    94  	)
    95  
    96  	w.Header().Set("Content-Type", "application/json")
    97  
    98  	if image != "" { //pull
    99  		if tag == "" {
   100  			image, tag = parsers.ParseRepositoryTag(image)
   101  		}
   102  		metaHeaders := map[string][]string{}
   103  		for k, v := range r.Header {
   104  			if strings.HasPrefix(k, "X-Meta-") {
   105  				metaHeaders[k] = v
   106  			}
   107  		}
   108  
   109  		imagePullConfig := &graph.ImagePullConfig{
   110  			MetaHeaders: metaHeaders,
   111  			AuthConfig:  authConfig,
   112  			OutStream:   output,
   113  		}
   114  
   115  		err = s.daemon.Repositories().Pull(image, tag, imagePullConfig)
   116  	} else { //import
   117  		if tag == "" {
   118  			repo, tag = parsers.ParseRepositoryTag(repo)
   119  		}
   120  
   121  		src := r.Form.Get("fromSrc")
   122  
   123  		// 'err' MUST NOT be defined within this block, we need any error
   124  		// generated from the download to be available to the output
   125  		// stream processing below
   126  		var newConfig *runconfig.Config
   127  		newConfig, err = builder.BuildFromConfig(s.daemon, &runconfig.Config{}, r.Form["changes"])
   128  		if err != nil {
   129  			return err
   130  		}
   131  
   132  		err = s.daemon.Repositories().Import(src, repo, tag, message, r.Body, output, newConfig)
   133  	}
   134  	if err != nil {
   135  		if !output.Flushed() {
   136  			return err
   137  		}
   138  		sf := streamformatter.NewJSONStreamFormatter()
   139  		output.Write(sf.FormatError(err))
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  func (s *Server) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   146  	if vars == nil {
   147  		return fmt.Errorf("Missing parameter")
   148  	}
   149  
   150  	metaHeaders := map[string][]string{}
   151  	for k, v := range r.Header {
   152  		if strings.HasPrefix(k, "X-Meta-") {
   153  			metaHeaders[k] = v
   154  		}
   155  	}
   156  	if err := parseForm(r); err != nil {
   157  		return err
   158  	}
   159  	authConfig := &cliconfig.AuthConfig{}
   160  
   161  	authEncoded := r.Header.Get("X-Registry-Auth")
   162  	if authEncoded != "" {
   163  		// the new format is to handle the authConfig as a header
   164  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   165  		if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
   166  			// to increase compatibility to existing api it is defaulting to be empty
   167  			authConfig = &cliconfig.AuthConfig{}
   168  		}
   169  	} else {
   170  		// the old format is supported for compatibility if there was no authConfig header
   171  		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
   172  			return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
   173  		}
   174  	}
   175  
   176  	name := vars["name"]
   177  	output := ioutils.NewWriteFlusher(w)
   178  	imagePushConfig := &graph.ImagePushConfig{
   179  		MetaHeaders: metaHeaders,
   180  		AuthConfig:  authConfig,
   181  		Tag:         r.Form.Get("tag"),
   182  		OutStream:   output,
   183  	}
   184  
   185  	w.Header().Set("Content-Type", "application/json")
   186  
   187  	if err := s.daemon.Repositories().Push(name, imagePushConfig); err != nil {
   188  		if !output.Flushed() {
   189  			return err
   190  		}
   191  		sf := streamformatter.NewJSONStreamFormatter()
   192  		output.Write(sf.FormatError(err))
   193  	}
   194  	return nil
   195  }
   196  
   197  func (s *Server) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   198  	if vars == nil {
   199  		return fmt.Errorf("Missing parameter")
   200  	}
   201  	if err := parseForm(r); err != nil {
   202  		return err
   203  	}
   204  
   205  	w.Header().Set("Content-Type", "application/x-tar")
   206  
   207  	output := ioutils.NewWriteFlusher(w)
   208  	var names []string
   209  	if name, ok := vars["name"]; ok {
   210  		names = []string{name}
   211  	} else {
   212  		names = r.Form["names"]
   213  	}
   214  
   215  	if err := s.daemon.Repositories().ImageExport(names, output); err != nil {
   216  		if !output.Flushed() {
   217  			return err
   218  		}
   219  		sf := streamformatter.NewJSONStreamFormatter()
   220  		output.Write(sf.FormatError(err))
   221  	}
   222  	return nil
   223  }
   224  
   225  func (s *Server) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   226  	return s.daemon.Repositories().Load(r.Body, w)
   227  }
   228  
   229  func (s *Server) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   230  	if err := parseForm(r); err != nil {
   231  		return err
   232  	}
   233  	if vars == nil {
   234  		return fmt.Errorf("Missing parameter")
   235  	}
   236  
   237  	name := vars["name"]
   238  
   239  	if name == "" {
   240  		return fmt.Errorf("image name cannot be blank")
   241  	}
   242  
   243  	force := boolValue(r, "force")
   244  	prune := !boolValue(r, "noprune")
   245  
   246  	list, err := s.daemon.ImageDelete(name, force, prune)
   247  	if err != nil {
   248  		return err
   249  	}
   250  
   251  	return writeJSON(w, http.StatusOK, list)
   252  }
   253  
   254  func (s *Server) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   255  	if vars == nil {
   256  		return fmt.Errorf("Missing parameter")
   257  	}
   258  
   259  	imageInspect, err := s.daemon.Repositories().Lookup(vars["name"])
   260  	if err != nil {
   261  		return err
   262  	}
   263  
   264  	return writeJSON(w, http.StatusOK, imageInspect)
   265  }
   266  
   267  func (s *Server) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   268  	var (
   269  		authConfigs        = map[string]cliconfig.AuthConfig{}
   270  		authConfigsEncoded = r.Header.Get("X-Registry-Config")
   271  		buildConfig        = builder.NewBuildConfig()
   272  	)
   273  
   274  	if authConfigsEncoded != "" {
   275  		authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
   276  		if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); err != nil {
   277  			// for a pull it is not an error if no auth was given
   278  			// to increase compatibility with the existing api it is defaulting
   279  			// to be empty.
   280  		}
   281  	}
   282  
   283  	w.Header().Set("Content-Type", "application/json")
   284  
   285  	version := ctx.Version()
   286  	if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
   287  		buildConfig.Remove = true
   288  	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
   289  		buildConfig.Remove = true
   290  	} else {
   291  		buildConfig.Remove = boolValue(r, "rm")
   292  	}
   293  	if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
   294  		buildConfig.Pull = true
   295  	}
   296  
   297  	output := ioutils.NewWriteFlusher(w)
   298  	buildConfig.Stdout = output
   299  	buildConfig.Context = r.Body
   300  
   301  	buildConfig.RemoteURL = r.FormValue("remote")
   302  	buildConfig.DockerfileName = r.FormValue("dockerfile")
   303  	buildConfig.RepoName = r.FormValue("t")
   304  	buildConfig.SuppressOutput = boolValue(r, "q")
   305  	buildConfig.NoCache = boolValue(r, "nocache")
   306  	buildConfig.ForceRemove = boolValue(r, "forcerm")
   307  	buildConfig.AuthConfigs = authConfigs
   308  	buildConfig.MemorySwap = int64ValueOrZero(r, "memswap")
   309  	buildConfig.Memory = int64ValueOrZero(r, "memory")
   310  	buildConfig.CPUShares = int64ValueOrZero(r, "cpushares")
   311  	buildConfig.CPUPeriod = int64ValueOrZero(r, "cpuperiod")
   312  	buildConfig.CPUQuota = int64ValueOrZero(r, "cpuquota")
   313  	buildConfig.CPUSetCpus = r.FormValue("cpusetcpus")
   314  	buildConfig.CPUSetMems = r.FormValue("cpusetmems")
   315  	buildConfig.CgroupParent = r.FormValue("cgroupparent")
   316  
   317  	var buildUlimits = []*ulimit.Ulimit{}
   318  	ulimitsJSON := r.FormValue("ulimits")
   319  	if ulimitsJSON != "" {
   320  		if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil {
   321  			return err
   322  		}
   323  		buildConfig.Ulimits = buildUlimits
   324  	}
   325  
   326  	var buildArgs = map[string]string{}
   327  	buildArgsJSON := r.FormValue("buildargs")
   328  	if buildArgsJSON != "" {
   329  		if err := json.NewDecoder(strings.NewReader(buildArgsJSON)).Decode(&buildArgs); err != nil {
   330  			return err
   331  		}
   332  	}
   333  	buildConfig.BuildArgs = buildArgs
   334  
   335  	// Job cancellation. Note: not all job types support this.
   336  	if closeNotifier, ok := w.(http.CloseNotifier); ok {
   337  		finished := make(chan struct{})
   338  		defer close(finished)
   339  		go func() {
   340  			select {
   341  			case <-finished:
   342  			case <-closeNotifier.CloseNotify():
   343  				logrus.Infof("Client disconnected, cancelling job: build")
   344  				buildConfig.Cancel()
   345  			}
   346  		}()
   347  	}
   348  
   349  	if err := builder.Build(s.daemon, buildConfig); err != nil {
   350  		// Do not write the error in the http output if it's still empty.
   351  		// This prevents from writing a 200(OK) when there is an interal error.
   352  		if !output.Flushed() {
   353  			return err
   354  		}
   355  		sf := streamformatter.NewJSONStreamFormatter()
   356  		w.Write(sf.FormatError(errors.New(utils.GetErrorMessage(err))))
   357  	}
   358  	return nil
   359  }
   360  
   361  func (s *Server) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   362  	if err := parseForm(r); err != nil {
   363  		return err
   364  	}
   365  
   366  	// FIXME: The filter parameter could just be a match filter
   367  	images, err := s.daemon.Repositories().Images(r.Form.Get("filters"), r.Form.Get("filter"), boolValue(r, "all"))
   368  	if err != nil {
   369  		return err
   370  	}
   371  
   372  	return writeJSON(w, http.StatusOK, images)
   373  }
   374  
   375  func (s *Server) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   376  	if vars == nil {
   377  		return fmt.Errorf("Missing parameter")
   378  	}
   379  
   380  	name := vars["name"]
   381  	history, err := s.daemon.Repositories().History(name)
   382  	if err != nil {
   383  		return err
   384  	}
   385  
   386  	return writeJSON(w, http.StatusOK, history)
   387  }
   388  
   389  func (s *Server) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   390  	if err := parseForm(r); err != nil {
   391  		return err
   392  	}
   393  	if vars == nil {
   394  		return fmt.Errorf("Missing parameter")
   395  	}
   396  
   397  	repo := r.Form.Get("repo")
   398  	tag := r.Form.Get("tag")
   399  	force := boolValue(r, "force")
   400  	name := vars["name"]
   401  	if err := s.daemon.Repositories().Tag(repo, tag, name, force); err != nil {
   402  		return err
   403  	}
   404  	s.daemon.EventsService.Log("tag", utils.ImageReference(repo, tag), "")
   405  	w.WriteHeader(http.StatusCreated)
   406  	return nil
   407  }
   408  
   409  func (s *Server) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   410  	if err := parseForm(r); err != nil {
   411  		return err
   412  	}
   413  	var (
   414  		config      *cliconfig.AuthConfig
   415  		authEncoded = r.Header.Get("X-Registry-Auth")
   416  		headers     = map[string][]string{}
   417  	)
   418  
   419  	if authEncoded != "" {
   420  		authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   421  		if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
   422  			// for a search it is not an error if no auth was given
   423  			// to increase compatibility with the existing api it is defaulting to be empty
   424  			config = &cliconfig.AuthConfig{}
   425  		}
   426  	}
   427  	for k, v := range r.Header {
   428  		if strings.HasPrefix(k, "X-Meta-") {
   429  			headers[k] = v
   430  		}
   431  	}
   432  	query, err := s.daemon.RegistryService.Search(r.Form.Get("term"), config, headers)
   433  	if err != nil {
   434  		return err
   435  	}
   436  	return writeJSON(w, http.StatusOK, query.Results)
   437  }