github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/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  		Filter: 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  	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
   159  	if err := s.backend.ContainerStart(vars["name"], hostConfig, validateHostname, checkpoint); err != nil {
   160  		return err
   161  	}
   162  
   163  	w.WriteHeader(http.StatusNoContent)
   164  	return nil
   165  }
   166  
   167  func (s *containerRouter) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   168  	if err := httputils.ParseForm(r); err != nil {
   169  		return err
   170  	}
   171  
   172  	var seconds *int
   173  	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
   174  		valSeconds, err := strconv.Atoi(tmpSeconds)
   175  		if err != nil {
   176  			return err
   177  		}
   178  		seconds = &valSeconds
   179  	}
   180  
   181  	if err := s.backend.ContainerStop(vars["name"], seconds); err != nil {
   182  		return err
   183  	}
   184  	w.WriteHeader(http.StatusNoContent)
   185  
   186  	return nil
   187  }
   188  
   189  type errContainerIsRunning interface {
   190  	ContainerIsRunning() bool
   191  }
   192  
   193  func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   194  	if err := httputils.ParseForm(r); err != nil {
   195  		return err
   196  	}
   197  
   198  	var sig syscall.Signal
   199  	name := vars["name"]
   200  
   201  	// If we have a signal, look at it. Otherwise, do nothing
   202  	if sigStr := r.Form.Get("signal"); sigStr != "" {
   203  		var err error
   204  		if sig, err = signal.ParseSignal(sigStr); err != nil {
   205  			return err
   206  		}
   207  	}
   208  
   209  	if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
   210  		var isStopped bool
   211  		if e, ok := err.(errContainerIsRunning); ok {
   212  			isStopped = !e.ContainerIsRunning()
   213  		}
   214  
   215  		// Return error that's not caused because the container is stopped.
   216  		// Return error if the container is not running and the api is >= 1.20
   217  		// to keep backwards compatibility.
   218  		version := httputils.VersionFromContext(ctx)
   219  		if versions.GreaterThanOrEqualTo(version, "1.20") || !isStopped {
   220  			return fmt.Errorf("Cannot kill container %s: %v", name, err)
   221  		}
   222  	}
   223  
   224  	w.WriteHeader(http.StatusNoContent)
   225  	return nil
   226  }
   227  
   228  func (s *containerRouter) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   229  	if err := httputils.ParseForm(r); err != nil {
   230  		return err
   231  	}
   232  
   233  	var seconds *int
   234  	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
   235  		valSeconds, err := strconv.Atoi(tmpSeconds)
   236  		if err != nil {
   237  			return err
   238  		}
   239  		seconds = &valSeconds
   240  	}
   241  
   242  	if err := s.backend.ContainerRestart(vars["name"], seconds); err != nil {
   243  		return err
   244  	}
   245  
   246  	w.WriteHeader(http.StatusNoContent)
   247  
   248  	return nil
   249  }
   250  
   251  func (s *containerRouter) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   252  	if err := httputils.ParseForm(r); err != nil {
   253  		return err
   254  	}
   255  
   256  	if err := s.backend.ContainerPause(vars["name"]); err != nil {
   257  		return err
   258  	}
   259  
   260  	w.WriteHeader(http.StatusNoContent)
   261  
   262  	return nil
   263  }
   264  
   265  func (s *containerRouter) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   266  	if err := httputils.ParseForm(r); err != nil {
   267  		return err
   268  	}
   269  
   270  	if err := s.backend.ContainerUnpause(vars["name"]); err != nil {
   271  		return err
   272  	}
   273  
   274  	w.WriteHeader(http.StatusNoContent)
   275  
   276  	return nil
   277  }
   278  
   279  func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   280  	status, err := s.backend.ContainerWait(vars["name"], -1*time.Second)
   281  	if err != nil {
   282  		return err
   283  	}
   284  
   285  	return httputils.WriteJSON(w, http.StatusOK, &types.ContainerWaitResponse{
   286  		StatusCode: status,
   287  	})
   288  }
   289  
   290  func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   291  	changes, err := s.backend.ContainerChanges(vars["name"])
   292  	if err != nil {
   293  		return err
   294  	}
   295  
   296  	return httputils.WriteJSON(w, http.StatusOK, changes)
   297  }
   298  
   299  func (s *containerRouter) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   300  	if err := httputils.ParseForm(r); err != nil {
   301  		return err
   302  	}
   303  
   304  	procList, err := s.backend.ContainerTop(vars["name"], r.Form.Get("ps_args"))
   305  	if err != nil {
   306  		return err
   307  	}
   308  
   309  	return httputils.WriteJSON(w, http.StatusOK, procList)
   310  }
   311  
   312  func (s *containerRouter) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   313  	if err := httputils.ParseForm(r); err != nil {
   314  		return err
   315  	}
   316  
   317  	name := vars["name"]
   318  	newName := r.Form.Get("name")
   319  	if err := s.backend.ContainerRename(name, newName); err != nil {
   320  		return err
   321  	}
   322  	w.WriteHeader(http.StatusNoContent)
   323  	return nil
   324  }
   325  
   326  func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   327  	if err := httputils.ParseForm(r); err != nil {
   328  		return err
   329  	}
   330  	if err := httputils.CheckForJSON(r); err != nil {
   331  		return err
   332  	}
   333  
   334  	version := httputils.VersionFromContext(ctx)
   335  	var updateConfig container.UpdateConfig
   336  
   337  	decoder := json.NewDecoder(r.Body)
   338  	if err := decoder.Decode(&updateConfig); err != nil {
   339  		return err
   340  	}
   341  
   342  	hostConfig := &container.HostConfig{
   343  		Resources:     updateConfig.Resources,
   344  		RestartPolicy: updateConfig.RestartPolicy,
   345  	}
   346  
   347  	name := vars["name"]
   348  	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
   349  	resp, err := s.backend.ContainerUpdate(name, hostConfig, validateHostname)
   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  	validateHostname := versions.GreaterThanOrEqualTo(version, "1.24")
   375  	ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
   376  		Name:             name,
   377  		Config:           config,
   378  		HostConfig:       hostConfig,
   379  		NetworkingConfig: networkingConfig,
   380  		AdjustCPUShares:  adjustCPUShares,
   381  	}, validateHostname)
   382  	if err != nil {
   383  		return err
   384  	}
   385  
   386  	return httputils.WriteJSON(w, http.StatusCreated, ccr)
   387  }
   388  
   389  func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   390  	if err := httputils.ParseForm(r); err != nil {
   391  		return err
   392  	}
   393  
   394  	name := vars["name"]
   395  	config := &types.ContainerRmConfig{
   396  		ForceRemove:  httputils.BoolValue(r, "force"),
   397  		RemoveVolume: httputils.BoolValue(r, "v"),
   398  		RemoveLink:   httputils.BoolValue(r, "link"),
   399  	}
   400  
   401  	if err := s.backend.ContainerRm(name, config); err != nil {
   402  		return err
   403  	}
   404  
   405  	w.WriteHeader(http.StatusNoContent)
   406  
   407  	return nil
   408  }
   409  
   410  func (s *containerRouter) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   411  	if err := httputils.ParseForm(r); err != nil {
   412  		return err
   413  	}
   414  
   415  	height, err := strconv.Atoi(r.Form.Get("h"))
   416  	if err != nil {
   417  		return err
   418  	}
   419  	width, err := strconv.Atoi(r.Form.Get("w"))
   420  	if err != nil {
   421  		return err
   422  	}
   423  
   424  	return s.backend.ContainerResize(vars["name"], height, width)
   425  }
   426  
   427  func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   428  	err := httputils.ParseForm(r)
   429  	if err != nil {
   430  		return err
   431  	}
   432  	containerName := vars["name"]
   433  
   434  	_, upgrade := r.Header["Upgrade"]
   435  	detachKeys := r.FormValue("detachKeys")
   436  
   437  	hijacker, ok := w.(http.Hijacker)
   438  	if !ok {
   439  		return fmt.Errorf("error attaching to container %s, hijack connection missing", containerName)
   440  	}
   441  
   442  	setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
   443  		conn, _, err := hijacker.Hijack()
   444  		if err != nil {
   445  			return nil, nil, nil, err
   446  		}
   447  
   448  		// set raw mode
   449  		conn.Write([]byte{})
   450  
   451  		if upgrade {
   452  			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")
   453  		} else {
   454  			fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   455  		}
   456  
   457  		closer := func() error {
   458  			httputils.CloseStreams(conn)
   459  			return nil
   460  		}
   461  		return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil
   462  	}
   463  
   464  	attachConfig := &backend.ContainerAttachConfig{
   465  		GetStreams: setupStreams,
   466  		UseStdin:   httputils.BoolValue(r, "stdin"),
   467  		UseStdout:  httputils.BoolValue(r, "stdout"),
   468  		UseStderr:  httputils.BoolValue(r, "stderr"),
   469  		Logs:       httputils.BoolValue(r, "logs"),
   470  		Stream:     httputils.BoolValue(r, "stream"),
   471  		DetachKeys: detachKeys,
   472  		MuxStreams: true,
   473  	}
   474  
   475  	if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil {
   476  		logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
   477  		// Remember to close stream if error happens
   478  		conn, _, errHijack := hijacker.Hijack()
   479  		if errHijack == nil {
   480  			statusCode := httputils.GetHTTPErrorStatusCode(err)
   481  			statusText := http.StatusText(statusCode)
   482  			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())
   483  			httputils.CloseStreams(conn)
   484  		} else {
   485  			logrus.Errorf("Error Hijacking: %v", err)
   486  		}
   487  	}
   488  	return nil
   489  }
   490  
   491  func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   492  	if err := httputils.ParseForm(r); err != nil {
   493  		return err
   494  	}
   495  	containerName := vars["name"]
   496  
   497  	var err error
   498  	detachKeys := r.FormValue("detachKeys")
   499  
   500  	done := make(chan struct{})
   501  	started := make(chan struct{})
   502  
   503  	setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
   504  		wsChan := make(chan *websocket.Conn)
   505  		h := func(conn *websocket.Conn) {
   506  			wsChan <- conn
   507  			<-done
   508  		}
   509  
   510  		srv := websocket.Server{Handler: h, Handshake: nil}
   511  		go func() {
   512  			close(started)
   513  			srv.ServeHTTP(w, r)
   514  		}()
   515  
   516  		conn := <-wsChan
   517  		return conn, conn, conn, nil
   518  	}
   519  
   520  	attachConfig := &backend.ContainerAttachConfig{
   521  		GetStreams: setupStreams,
   522  		Logs:       httputils.BoolValue(r, "logs"),
   523  		Stream:     httputils.BoolValue(r, "stream"),
   524  		DetachKeys: detachKeys,
   525  		UseStdin:   true,
   526  		UseStdout:  true,
   527  		UseStderr:  true,
   528  		MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr
   529  	}
   530  
   531  	err = s.backend.ContainerAttach(containerName, attachConfig)
   532  	close(done)
   533  	select {
   534  	case <-started:
   535  		logrus.Errorf("Error attaching websocket: %s", err)
   536  		return nil
   537  	default:
   538  	}
   539  	return err
   540  }
   541  
   542  func (s *containerRouter) postContainersPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   543  	if err := httputils.ParseForm(r); err != nil {
   544  		return err
   545  	}
   546  
   547  	if err := httputils.CheckForJSON(r); err != nil {
   548  		return err
   549  	}
   550  
   551  	var cfg types.ContainersPruneConfig
   552  	if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
   553  		return err
   554  	}
   555  
   556  	pruneReport, err := s.backend.ContainersPrune(&cfg)
   557  	if err != nil {
   558  		return err
   559  	}
   560  	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
   561  }