github.com/a4a881d4/docker@v1.9.0-rc2/api/server/router/local/container.go (about)

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