github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/api/server/router/container/container_routes.go (about)

     1  package container
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http"
     7  	"strconv"
     8  	"strings"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  	"github.com/docker/distribution/registry/api/errcode"
    14  	"github.com/docker/docker/api/server/httputils"
    15  	"github.com/docker/docker/api/types"
    16  	timetypes "github.com/docker/docker/api/types/time"
    17  	"github.com/docker/docker/daemon"
    18  	derr "github.com/docker/docker/errors"
    19  	"github.com/docker/docker/pkg/ioutils"
    20  	"github.com/docker/docker/pkg/signal"
    21  	"github.com/docker/docker/runconfig"
    22  	"github.com/docker/docker/utils"
    23  	"golang.org/x/net/context"
    24  	"golang.org/x/net/websocket"
    25  )
    26  
    27  func (s *containerRouter) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    28  	if err := httputils.ParseForm(r); err != nil {
    29  		return err
    30  	}
    31  
    32  	config := &daemon.ContainersConfig{
    33  		All:     httputils.BoolValue(r, "all"),
    34  		Size:    httputils.BoolValue(r, "size"),
    35  		Since:   r.Form.Get("since"),
    36  		Before:  r.Form.Get("before"),
    37  		Filters: r.Form.Get("filters"),
    38  	}
    39  
    40  	if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
    41  		limit, err := strconv.Atoi(tmpLimit)
    42  		if err != nil {
    43  			return err
    44  		}
    45  		config.Limit = limit
    46  	}
    47  
    48  	containers, err := s.backend.Containers(config)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	return httputils.WriteJSON(w, http.StatusOK, containers)
    54  }
    55  
    56  func (s *containerRouter) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    57  	if err := httputils.ParseForm(r); err != nil {
    58  		return err
    59  	}
    60  
    61  	stream := httputils.BoolValueOrDefault(r, "stream", true)
    62  	var out io.Writer
    63  	if !stream {
    64  		w.Header().Set("Content-Type", "application/json")
    65  		out = w
    66  	} else {
    67  		wf := ioutils.NewWriteFlusher(w)
    68  		out = wf
    69  		defer wf.Close()
    70  	}
    71  
    72  	var closeNotifier <-chan bool
    73  	if notifier, ok := w.(http.CloseNotifier); ok {
    74  		closeNotifier = notifier.CloseNotify()
    75  	}
    76  
    77  	config := &daemon.ContainerStatsConfig{
    78  		Stream:    stream,
    79  		OutStream: out,
    80  		Stop:      closeNotifier,
    81  		Version:   httputils.VersionFromContext(ctx),
    82  	}
    83  
    84  	return s.backend.ContainerStats(vars["name"], config)
    85  }
    86  
    87  func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    88  	if err := httputils.ParseForm(r); err != nil {
    89  		return err
    90  	}
    91  
    92  	// Args are validated before the stream starts because when it starts we're
    93  	// sending HTTP 200 by writing an empty chunk of data to tell the client that
    94  	// daemon is going to stream. By sending this initial HTTP 200 we can't report
    95  	// any error after the stream starts (i.e. container not found, wrong parameters)
    96  	// with the appropriate status code.
    97  	stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr")
    98  	if !(stdout || stderr) {
    99  		return fmt.Errorf("Bad parameters: you must choose at least one stream")
   100  	}
   101  
   102  	var since time.Time
   103  	if r.Form.Get("since") != "" {
   104  		s, n, err := timetypes.ParseTimestamps(r.Form.Get("since"), 0)
   105  		if err != nil {
   106  			return err
   107  		}
   108  		since = time.Unix(s, n)
   109  	}
   110  
   111  	var closeNotifier <-chan bool
   112  	if notifier, ok := w.(http.CloseNotifier); ok {
   113  		closeNotifier = notifier.CloseNotify()
   114  	}
   115  
   116  	containerName := vars["name"]
   117  
   118  	if !s.backend.Exists(containerName) {
   119  		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
   120  	}
   121  
   122  	// write an empty chunk of data (this is to ensure that the
   123  	// HTTP Response is sent immediately, even if the container has
   124  	// not yet produced any data)
   125  	w.WriteHeader(http.StatusOK)
   126  	if flusher, ok := w.(http.Flusher); ok {
   127  		flusher.Flush()
   128  	}
   129  
   130  	output := ioutils.NewWriteFlusher(w)
   131  	defer output.Close()
   132  
   133  	logsConfig := &daemon.ContainerLogsConfig{
   134  		Follow:     httputils.BoolValue(r, "follow"),
   135  		Timestamps: httputils.BoolValue(r, "timestamps"),
   136  		Since:      since,
   137  		Tail:       r.Form.Get("tail"),
   138  		UseStdout:  stdout,
   139  		UseStderr:  stderr,
   140  		OutStream:  output,
   141  		Stop:       closeNotifier,
   142  	}
   143  
   144  	if err := s.backend.ContainerLogs(containerName, logsConfig); err != nil {
   145  		// The client may be expecting all of the data we're sending to
   146  		// be multiplexed, so send it through OutStream, which will
   147  		// have been set up to handle that if needed.
   148  		fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err))
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   155  	return s.backend.ContainerExport(vars["name"], w)
   156  }
   157  
   158  func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   159  	// If contentLength is -1, we can assumed chunked encoding
   160  	// or more technically that the length is unknown
   161  	// https://golang.org/src/pkg/net/http/request.go#L139
   162  	// net/http otherwise seems to swallow any headers related to chunked encoding
   163  	// including r.TransferEncoding
   164  	// allow a nil body for backwards compatibility
   165  	var hostConfig *runconfig.HostConfig
   166  	if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
   167  		if err := httputils.CheckForJSON(r); err != nil {
   168  			return err
   169  		}
   170  
   171  		c, err := runconfig.DecodeHostConfig(r.Body)
   172  		if err != nil {
   173  			return err
   174  		}
   175  
   176  		hostConfig = c
   177  	}
   178  
   179  	if err := s.backend.ContainerStart(vars["name"], hostConfig); err != nil {
   180  		return err
   181  	}
   182  	w.WriteHeader(http.StatusNoContent)
   183  	return nil
   184  }
   185  
   186  func (s *containerRouter) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   187  	if err := httputils.ParseForm(r); err != nil {
   188  		return err
   189  	}
   190  
   191  	seconds, _ := strconv.Atoi(r.Form.Get("t"))
   192  
   193  	if err := s.backend.ContainerStop(vars["name"], seconds); err != nil {
   194  		return err
   195  	}
   196  	w.WriteHeader(http.StatusNoContent)
   197  
   198  	return nil
   199  }
   200  
   201  func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   202  	if err := httputils.ParseForm(r); err != nil {
   203  		return err
   204  	}
   205  
   206  	var sig syscall.Signal
   207  	name := vars["name"]
   208  
   209  	// If we have a signal, look at it. Otherwise, do nothing
   210  	if sigStr := r.Form.Get("signal"); sigStr != "" {
   211  		var err error
   212  		if sig, err = signal.ParseSignal(sigStr); err != nil {
   213  			return err
   214  		}
   215  	}
   216  
   217  	if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
   218  		theErr, isDerr := err.(errcode.ErrorCoder)
   219  		isStopped := isDerr && theErr.ErrorCode() == derr.ErrorCodeNotRunning
   220  
   221  		// Return error that's not caused because the container is stopped.
   222  		// Return error if the container is not running and the api is >= 1.20
   223  		// to keep backwards compatibility.
   224  		version := httputils.VersionFromContext(ctx)
   225  		if version.GreaterThanOrEqualTo("1.20") || !isStopped {
   226  			return fmt.Errorf("Cannot kill container %s: %v", name, err)
   227  		}
   228  	}
   229  
   230  	w.WriteHeader(http.StatusNoContent)
   231  	return nil
   232  }
   233  
   234  func (s *containerRouter) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   235  	if err := httputils.ParseForm(r); err != nil {
   236  		return err
   237  	}
   238  
   239  	timeout, _ := strconv.Atoi(r.Form.Get("t"))
   240  
   241  	if err := s.backend.ContainerRestart(vars["name"], timeout); err != nil {
   242  		return err
   243  	}
   244  
   245  	w.WriteHeader(http.StatusNoContent)
   246  
   247  	return nil
   248  }
   249  
   250  func (s *containerRouter) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   251  	if err := httputils.ParseForm(r); err != nil {
   252  		return err
   253  	}
   254  
   255  	if err := s.backend.ContainerPause(vars["name"]); err != nil {
   256  		return err
   257  	}
   258  
   259  	w.WriteHeader(http.StatusNoContent)
   260  
   261  	return nil
   262  }
   263  
   264  func (s *containerRouter) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   265  	if err := httputils.ParseForm(r); err != nil {
   266  		return err
   267  	}
   268  
   269  	if err := s.backend.ContainerUnpause(vars["name"]); err != nil {
   270  		return err
   271  	}
   272  
   273  	w.WriteHeader(http.StatusNoContent)
   274  
   275  	return nil
   276  }
   277  
   278  func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   279  	status, err := s.backend.ContainerWait(vars["name"], -1*time.Second)
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	return httputils.WriteJSON(w, http.StatusOK, &types.ContainerWaitResponse{
   285  		StatusCode: status,
   286  	})
   287  }
   288  
   289  func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   290  	changes, err := s.backend.ContainerChanges(vars["name"])
   291  	if err != nil {
   292  		return err
   293  	}
   294  
   295  	return httputils.WriteJSON(w, http.StatusOK, changes)
   296  }
   297  
   298  func (s *containerRouter) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   299  	if err := httputils.ParseForm(r); err != nil {
   300  		return err
   301  	}
   302  
   303  	procList, err := s.backend.ContainerTop(vars["name"], r.Form.Get("ps_args"))
   304  	if err != nil {
   305  		return err
   306  	}
   307  
   308  	return httputils.WriteJSON(w, http.StatusOK, procList)
   309  }
   310  
   311  func (s *containerRouter) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   312  	if err := httputils.ParseForm(r); err != nil {
   313  		return err
   314  	}
   315  
   316  	name := vars["name"]
   317  	newName := r.Form.Get("name")
   318  	if err := s.backend.ContainerRename(name, newName); err != nil {
   319  		return err
   320  	}
   321  	w.WriteHeader(http.StatusNoContent)
   322  	return nil
   323  }
   324  
   325  func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   326  	if err := httputils.ParseForm(r); err != nil {
   327  		return err
   328  	}
   329  	if err := httputils.CheckForJSON(r); err != nil {
   330  		return err
   331  	}
   332  
   333  	name := r.Form.Get("name")
   334  
   335  	config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
   336  	if err != nil {
   337  		return err
   338  	}
   339  	version := httputils.VersionFromContext(ctx)
   340  	adjustCPUShares := version.LessThan("1.19")
   341  
   342  	ccr, err := s.backend.ContainerCreate(&daemon.ContainerCreateConfig{
   343  		Name:            name,
   344  		Config:          config,
   345  		HostConfig:      hostConfig,
   346  		AdjustCPUShares: adjustCPUShares,
   347  	})
   348  	if err != nil {
   349  		return err
   350  	}
   351  
   352  	return httputils.WriteJSON(w, http.StatusCreated, ccr)
   353  }
   354  
   355  func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   356  	if err := httputils.ParseForm(r); err != nil {
   357  		return err
   358  	}
   359  
   360  	name := vars["name"]
   361  	config := &types.ContainerRmConfig{
   362  		ForceRemove:  httputils.BoolValue(r, "force"),
   363  		RemoveVolume: httputils.BoolValue(r, "v"),
   364  		RemoveLink:   httputils.BoolValue(r, "link"),
   365  	}
   366  
   367  	if err := s.backend.ContainerRm(name, config); err != nil {
   368  		// Force a 404 for the empty string
   369  		if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") {
   370  			return fmt.Errorf("no such container: \"\"")
   371  		}
   372  		return err
   373  	}
   374  
   375  	w.WriteHeader(http.StatusNoContent)
   376  
   377  	return nil
   378  }
   379  
   380  func (s *containerRouter) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   381  	if err := httputils.ParseForm(r); err != nil {
   382  		return err
   383  	}
   384  
   385  	height, err := strconv.Atoi(r.Form.Get("h"))
   386  	if err != nil {
   387  		return err
   388  	}
   389  	width, err := strconv.Atoi(r.Form.Get("w"))
   390  	if err != nil {
   391  		return err
   392  	}
   393  
   394  	return s.backend.ContainerResize(vars["name"], height, width)
   395  }
   396  
   397  func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   398  	if err := httputils.ParseForm(r); err != nil {
   399  		return err
   400  	}
   401  	containerName := vars["name"]
   402  
   403  	if !s.backend.Exists(containerName) {
   404  		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
   405  	}
   406  
   407  	if s.backend.IsPaused(containerName) {
   408  		return derr.ErrorCodePausedContainer.WithArgs(containerName)
   409  	}
   410  
   411  	inStream, outStream, err := httputils.HijackConnection(w)
   412  	if err != nil {
   413  		return err
   414  	}
   415  	defer httputils.CloseStreams(inStream, outStream)
   416  
   417  	if _, ok := r.Header["Upgrade"]; ok {
   418  		fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
   419  	} else {
   420  		fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   421  	}
   422  
   423  	attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
   424  		InStream:  inStream,
   425  		OutStream: outStream,
   426  		UseStdin:  httputils.BoolValue(r, "stdin"),
   427  		UseStdout: httputils.BoolValue(r, "stdout"),
   428  		UseStderr: httputils.BoolValue(r, "stderr"),
   429  		Logs:      httputils.BoolValue(r, "logs"),
   430  		Stream:    httputils.BoolValue(r, "stream"),
   431  	}
   432  
   433  	if err := s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil {
   434  		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
   435  	}
   436  
   437  	return nil
   438  }
   439  
   440  func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   441  	if err := httputils.ParseForm(r); err != nil {
   442  		return err
   443  	}
   444  	containerName := vars["name"]
   445  
   446  	if !s.backend.Exists(containerName) {
   447  		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
   448  	}
   449  
   450  	h := websocket.Handler(func(ws *websocket.Conn) {
   451  		defer ws.Close()
   452  
   453  		wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{
   454  			InStream:  ws,
   455  			OutStream: ws,
   456  			ErrStream: ws,
   457  			Logs:      httputils.BoolValue(r, "logs"),
   458  			Stream:    httputils.BoolValue(r, "stream"),
   459  		}
   460  
   461  		if err := s.backend.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {
   462  			logrus.Errorf("Error attaching websocket: %s", err)
   463  		}
   464  	})
   465  	ws := websocket.Server{Handler: h, Handshake: nil}
   466  	ws.ServeHTTP(w, r)
   467  
   468  	return nil
   469  }