github.com/rsampaio/docker@v0.7.2-0.20150827203920-fdc73cc3fc31/api/server/image.go (about)

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