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