github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/api/server/router/container/container_routes.go (about)

     1  package container
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"strconv"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  	"github.com/docker/docker/api/server/httputils"
    14  	"github.com/docker/docker/api/types"
    15  	"github.com/docker/docker/api/types/backend"
    16  	"github.com/docker/docker/api/types/container"
    17  	"github.com/docker/docker/api/types/filters"
    18  	"github.com/docker/docker/api/types/versions"
    19  	"github.com/docker/docker/pkg/ioutils"
    20  	"github.com/docker/docker/pkg/signal"
    21  	"github.com/docker/docker/pkg/stdcopy"
    22  	"golang.org/x/net/context"
    23  	"golang.org/x/net/websocket"
    24  )
    25  
    26  func (s *containerRouter) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    27  	if err := httputils.ParseForm(r); err != nil {
    28  		return err
    29  	}
    30  	filter, err := filters.FromParam(r.Form.Get("filters"))
    31  	if err != nil {
    32  		return err
    33  	}
    34  
    35  	config := &types.ContainerListOptions{
    36  		All:     httputils.BoolValue(r, "all"),
    37  		Size:    httputils.BoolValue(r, "size"),
    38  		Since:   r.Form.Get("since"),
    39  		Before:  r.Form.Get("before"),
    40  		Filters: filter,
    41  	}
    42  
    43  	if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
    44  		limit, err := strconv.Atoi(tmpLimit)
    45  		if err != nil {
    46  			return err
    47  		}
    48  		config.Limit = limit
    49  	}
    50  
    51  	containers, err := s.backend.Containers(config)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	return httputils.WriteJSON(w, http.StatusOK, containers)
    57  }
    58  
    59  func (s *containerRouter) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    60  	if err := httputils.ParseForm(r); err != nil {
    61  		return err
    62  	}
    63  
    64  	stream := httputils.BoolValueOrDefault(r, "stream", true)
    65  	if !stream {
    66  		w.Header().Set("Content-Type", "application/json")
    67  	}
    68  
    69  	config := &backend.ContainerStatsConfig{
    70  		Stream:    stream,
    71  		OutStream: w,
    72  		Version:   string(httputils.VersionFromContext(ctx)),
    73  	}
    74  
    75  	return s.backend.ContainerStats(ctx, vars["name"], config)
    76  }
    77  
    78  func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    79  	if err := httputils.ParseForm(r); err != nil {
    80  		return err
    81  	}
    82  
    83  	// Args are validated before the stream starts because when it starts we're
    84  	// sending HTTP 200 by writing an empty chunk of data to tell the client that
    85  	// daemon is going to stream. By sending this initial HTTP 200 we can't report
    86  	// any error after the stream starts (i.e. container not found, wrong parameters)
    87  	// with the appropriate status code.
    88  	stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr")
    89  	if !(stdout || stderr) {
    90  		return fmt.Errorf("Bad parameters: you must choose at least one stream")
    91  	}
    92  
    93  	containerName := vars["name"]
    94  	logsConfig := &backend.ContainerLogsConfig{
    95  		ContainerLogsOptions: types.ContainerLogsOptions{
    96  			Follow:     httputils.BoolValue(r, "follow"),
    97  			Timestamps: httputils.BoolValue(r, "timestamps"),
    98  			Since:      r.Form.Get("since"),
    99  			Tail:       r.Form.Get("tail"),
   100  			ShowStdout: stdout,
   101  			ShowStderr: stderr,
   102  			Details:    httputils.BoolValue(r, "details"),
   103  		},
   104  		OutStream: w,
   105  	}
   106  
   107  	chStarted := make(chan struct{})
   108  	if err := s.backend.ContainerLogs(ctx, containerName, logsConfig, chStarted); err != nil {
   109  		select {
   110  		case <-chStarted:
   111  			// The client may be expecting all of the data we're sending to
   112  			// be multiplexed, so mux it through the Systemerr stream, which
   113  			// will cause the client to throw an error when demuxing
   114  			stdwriter := stdcopy.NewStdWriter(logsConfig.OutStream, stdcopy.Systemerr)
   115  			fmt.Fprintf(stdwriter, "Error running logs job: %v\n", err)
   116  		default:
   117  			return err
   118  		}
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   125  	return s.backend.ContainerExport(vars["name"], w)
   126  }
   127  
   128  func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   129  	// If contentLength is -1, we can assumed chunked encoding
   130  	// or more technically that the length is unknown
   131  	// https://golang.org/src/pkg/net/http/request.go#L139
   132  	// net/http otherwise seems to swallow any headers related to chunked encoding
   133  	// including r.TransferEncoding
   134  	// allow a nil body for backwards compatibility
   135  
   136  	version := httputils.VersionFromContext(ctx)
   137  	var hostConfig *container.HostConfig
   138  	// A non-nil json object is at least 7 characters.
   139  	if r.ContentLength > 7 || r.ContentLength == -1 {
   140  		if versions.GreaterThanOrEqualTo(version, "1.24") {
   141  			return validationError{fmt.Errorf("starting container with non-empty request body was deprecated since v1.10 and removed in v1.12")}
   142  		}
   143  
   144  		if err := httputils.CheckForJSON(r); err != nil {
   145  			return err
   146  		}
   147  
   148  		c, err := s.decoder.DecodeHostConfig(r.Body)
   149  		if err != nil {
   150  			return err
   151  		}
   152  		hostConfig = c
   153  	}
   154  
   155  	if err := httputils.ParseForm(r); err != nil {
   156  		return err
   157  	}
   158  
   159  	checkpoint := r.Form.Get("checkpoint")
   160  	checkpointDir := r.Form.Get("checkpoint-dir")
   161  	if err := s.backend.ContainerStart(vars["name"], hostConfig, checkpoint, checkpointDir); err != nil {
   162  		return err
   163  	}
   164  
   165  	w.WriteHeader(http.StatusNoContent)
   166  	return nil
   167  }
   168  
   169  func (s *containerRouter) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   170  	if err := httputils.ParseForm(r); err != nil {
   171  		return err
   172  	}
   173  
   174  	var seconds *int
   175  	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
   176  		valSeconds, err := strconv.Atoi(tmpSeconds)
   177  		if err != nil {
   178  			return err
   179  		}
   180  		seconds = &valSeconds
   181  	}
   182  
   183  	if err := s.backend.ContainerStop(vars["name"], seconds); err != nil {
   184  		return err
   185  	}
   186  	w.WriteHeader(http.StatusNoContent)
   187  
   188  	return nil
   189  }
   190  
   191  type errContainerIsRunning interface {
   192  	ContainerIsRunning() bool
   193  }
   194  
   195  func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   196  	if err := httputils.ParseForm(r); err != nil {
   197  		return err
   198  	}
   199  
   200  	var sig syscall.Signal
   201  	name := vars["name"]
   202  
   203  	// If we have a signal, look at it. Otherwise, do nothing
   204  	if sigStr := r.Form.Get("signal"); sigStr != "" {
   205  		var err error
   206  		if sig, err = signal.ParseSignal(sigStr); err != nil {
   207  			return err
   208  		}
   209  	}
   210  
   211  	if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
   212  		var isStopped bool
   213  		if e, ok := err.(errContainerIsRunning); ok {
   214  			isStopped = !e.ContainerIsRunning()
   215  		}
   216  
   217  		// Return error that's not caused because the container is stopped.
   218  		// Return error if the container is not running and the api is >= 1.20
   219  		// to keep backwards compatibility.
   220  		version := httputils.VersionFromContext(ctx)
   221  		if versions.GreaterThanOrEqualTo(version, "1.20") || !isStopped {
   222  			return fmt.Errorf("Cannot kill container %s: %v", name, err)
   223  		}
   224  	}
   225  
   226  	w.WriteHeader(http.StatusNoContent)
   227  	return nil
   228  }
   229  
   230  func (s *containerRouter) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   231  	if err := httputils.ParseForm(r); err != nil {
   232  		return err
   233  	}
   234  
   235  	var seconds *int
   236  	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
   237  		valSeconds, err := strconv.Atoi(tmpSeconds)
   238  		if err != nil {
   239  			return err
   240  		}
   241  		seconds = &valSeconds
   242  	}
   243  
   244  	if err := s.backend.ContainerRestart(vars["name"], seconds); err != nil {
   245  		return err
   246  	}
   247  
   248  	w.WriteHeader(http.StatusNoContent)
   249  
   250  	return nil
   251  }
   252  
   253  func (s *containerRouter) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   254  	if err := httputils.ParseForm(r); err != nil {
   255  		return err
   256  	}
   257  
   258  	if err := s.backend.ContainerPause(vars["name"]); err != nil {
   259  		return err
   260  	}
   261  
   262  	w.WriteHeader(http.StatusNoContent)
   263  
   264  	return nil
   265  }
   266  
   267  func (s *containerRouter) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   268  	if err := httputils.ParseForm(r); err != nil {
   269  		return err
   270  	}
   271  
   272  	if err := s.backend.ContainerUnpause(vars["name"]); err != nil {
   273  		return err
   274  	}
   275  
   276  	w.WriteHeader(http.StatusNoContent)
   277  
   278  	return nil
   279  }
   280  
   281  func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   282  	status, err := s.backend.ContainerWait(vars["name"], -1*time.Second)
   283  	if err != nil {
   284  		return err
   285  	}
   286  
   287  	return httputils.WriteJSON(w, http.StatusOK, &container.ContainerWaitOKBody{
   288  		StatusCode: int64(status),
   289  	})
   290  }
   291  
   292  func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   293  	changes, err := s.backend.ContainerChanges(vars["name"])
   294  	if err != nil {
   295  		return err
   296  	}
   297  
   298  	return httputils.WriteJSON(w, http.StatusOK, changes)
   299  }
   300  
   301  func (s *containerRouter) getContainersTop(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  	procList, err := s.backend.ContainerTop(vars["name"], r.Form.Get("ps_args"))
   307  	if err != nil {
   308  		return err
   309  	}
   310  
   311  	return httputils.WriteJSON(w, http.StatusOK, procList)
   312  }
   313  
   314  func (s *containerRouter) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   315  	if err := httputils.ParseForm(r); err != nil {
   316  		return err
   317  	}
   318  
   319  	name := vars["name"]
   320  	newName := r.Form.Get("name")
   321  	if err := s.backend.ContainerRename(name, newName); err != nil {
   322  		return err
   323  	}
   324  	w.WriteHeader(http.StatusNoContent)
   325  	return nil
   326  }
   327  
   328  func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   329  	if err := httputils.ParseForm(r); err != nil {
   330  		return err
   331  	}
   332  	if err := httputils.CheckForJSON(r); err != nil {
   333  		return err
   334  	}
   335  
   336  	var updateConfig container.UpdateConfig
   337  
   338  	decoder := json.NewDecoder(r.Body)
   339  	if err := decoder.Decode(&updateConfig); err != nil {
   340  		return err
   341  	}
   342  
   343  	hostConfig := &container.HostConfig{
   344  		Resources:     updateConfig.Resources,
   345  		RestartPolicy: updateConfig.RestartPolicy,
   346  	}
   347  
   348  	name := vars["name"]
   349  	resp, err := s.backend.ContainerUpdate(name, hostConfig)
   350  	if err != nil {
   351  		return err
   352  	}
   353  
   354  	return httputils.WriteJSON(w, http.StatusOK, resp)
   355  }
   356  
   357  func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   358  	if err := httputils.ParseForm(r); err != nil {
   359  		return err
   360  	}
   361  	if err := httputils.CheckForJSON(r); err != nil {
   362  		return err
   363  	}
   364  
   365  	name := r.Form.Get("name")
   366  
   367  	config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body)
   368  	if err != nil {
   369  		return err
   370  	}
   371  	version := httputils.VersionFromContext(ctx)
   372  	adjustCPUShares := versions.LessThan(version, "1.19")
   373  
   374  	// When using API 1.24 and under, the client is responsible for removing the container
   375  	if hostConfig != nil && versions.LessThan(version, "1.25") {
   376  		hostConfig.AutoRemove = false
   377  	}
   378  
   379  	ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
   380  		Name:             name,
   381  		Config:           config,
   382  		HostConfig:       hostConfig,
   383  		NetworkingConfig: networkingConfig,
   384  		AdjustCPUShares:  adjustCPUShares,
   385  	})
   386  	if err != nil {
   387  		return err
   388  	}
   389  
   390  	return httputils.WriteJSON(w, http.StatusCreated, ccr)
   391  }
   392  
   393  func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   394  	if err := httputils.ParseForm(r); err != nil {
   395  		return err
   396  	}
   397  
   398  	name := vars["name"]
   399  	config := &types.ContainerRmConfig{
   400  		ForceRemove:  httputils.BoolValue(r, "force"),
   401  		RemoveVolume: httputils.BoolValue(r, "v"),
   402  		RemoveLink:   httputils.BoolValue(r, "link"),
   403  	}
   404  
   405  	if err := s.backend.ContainerRm(name, config); err != nil {
   406  		return err
   407  	}
   408  
   409  	w.WriteHeader(http.StatusNoContent)
   410  
   411  	return nil
   412  }
   413  
   414  func (s *containerRouter) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   415  	if err := httputils.ParseForm(r); err != nil {
   416  		return err
   417  	}
   418  
   419  	height, err := strconv.Atoi(r.Form.Get("h"))
   420  	if err != nil {
   421  		return err
   422  	}
   423  	width, err := strconv.Atoi(r.Form.Get("w"))
   424  	if err != nil {
   425  		return err
   426  	}
   427  
   428  	return s.backend.ContainerResize(vars["name"], height, width)
   429  }
   430  
   431  func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   432  	err := httputils.ParseForm(r)
   433  	if err != nil {
   434  		return err
   435  	}
   436  	containerName := vars["name"]
   437  
   438  	_, upgrade := r.Header["Upgrade"]
   439  	detachKeys := r.FormValue("detachKeys")
   440  
   441  	hijacker, ok := w.(http.Hijacker)
   442  	if !ok {
   443  		return fmt.Errorf("error attaching to container %s, hijack connection missing", containerName)
   444  	}
   445  
   446  	setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
   447  		conn, _, err := hijacker.Hijack()
   448  		if err != nil {
   449  			return nil, nil, nil, err
   450  		}
   451  
   452  		// set raw mode
   453  		conn.Write([]byte{})
   454  
   455  		if upgrade {
   456  			fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
   457  		} else {
   458  			fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   459  		}
   460  
   461  		closer := func() error {
   462  			httputils.CloseStreams(conn)
   463  			return nil
   464  		}
   465  		return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil
   466  	}
   467  
   468  	attachConfig := &backend.ContainerAttachConfig{
   469  		GetStreams: setupStreams,
   470  		UseStdin:   httputils.BoolValue(r, "stdin"),
   471  		UseStdout:  httputils.BoolValue(r, "stdout"),
   472  		UseStderr:  httputils.BoolValue(r, "stderr"),
   473  		Logs:       httputils.BoolValue(r, "logs"),
   474  		Stream:     httputils.BoolValue(r, "stream"),
   475  		DetachKeys: detachKeys,
   476  		MuxStreams: true,
   477  	}
   478  
   479  	if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil {
   480  		logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
   481  		// Remember to close stream if error happens
   482  		conn, _, errHijack := hijacker.Hijack()
   483  		if errHijack == nil {
   484  			statusCode := httputils.GetHTTPErrorStatusCode(err)
   485  			statusText := http.StatusText(statusCode)
   486  			fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n%s\r\n", statusCode, statusText, err.Error())
   487  			httputils.CloseStreams(conn)
   488  		} else {
   489  			logrus.Errorf("Error Hijacking: %v", err)
   490  		}
   491  	}
   492  	return nil
   493  }
   494  
   495  func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   496  	if err := httputils.ParseForm(r); err != nil {
   497  		return err
   498  	}
   499  	containerName := vars["name"]
   500  
   501  	var err error
   502  	detachKeys := r.FormValue("detachKeys")
   503  
   504  	done := make(chan struct{})
   505  	started := make(chan struct{})
   506  
   507  	version := httputils.VersionFromContext(ctx)
   508  
   509  	setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
   510  		wsChan := make(chan *websocket.Conn)
   511  		h := func(conn *websocket.Conn) {
   512  			wsChan <- conn
   513  			<-done
   514  		}
   515  
   516  		srv := websocket.Server{Handler: h, Handshake: nil}
   517  		go func() {
   518  			close(started)
   519  			srv.ServeHTTP(w, r)
   520  		}()
   521  
   522  		conn := <-wsChan
   523  		// In case version is higher than 1.27, a binary frame will be sent.
   524  		// See 28176 for details.
   525  		if versions.GreaterThanOrEqualTo(version, "1.27") {
   526  			conn.PayloadType = websocket.BinaryFrame
   527  		}
   528  		return conn, conn, conn, nil
   529  	}
   530  
   531  	attachConfig := &backend.ContainerAttachConfig{
   532  		GetStreams: setupStreams,
   533  		Logs:       httputils.BoolValue(r, "logs"),
   534  		Stream:     httputils.BoolValue(r, "stream"),
   535  		DetachKeys: detachKeys,
   536  		UseStdin:   true,
   537  		UseStdout:  true,
   538  		UseStderr:  true,
   539  		MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr
   540  	}
   541  
   542  	err = s.backend.ContainerAttach(containerName, attachConfig)
   543  	close(done)
   544  	select {
   545  	case <-started:
   546  		logrus.Errorf("Error attaching websocket: %s", err)
   547  		return nil
   548  	default:
   549  	}
   550  	return err
   551  }
   552  
   553  func (s *containerRouter) postContainersPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   554  	if err := httputils.ParseForm(r); err != nil {
   555  		return err
   556  	}
   557  
   558  	pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
   559  	if err != nil {
   560  		return err
   561  	}
   562  
   563  	pruneReport, err := s.backend.ContainersPrune(pruneFilters)
   564  	if err != nil {
   565  		return err
   566  	}
   567  	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
   568  }