github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/api/server/container.go (about)

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