github.com/akashshinde/docker@v1.9.1/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  		wf := ioutils.NewWriteFlusher(w)
    70  		out = wf
    71  		defer wf.Close()
    72  	}
    73  
    74  	var closeNotifier <-chan bool
    75  	if notifier, ok := w.(http.CloseNotifier); ok {
    76  		closeNotifier = notifier.CloseNotify()
    77  	}
    78  
    79  	config := &daemon.ContainerStatsConfig{
    80  		Stream:    stream,
    81  		OutStream: out,
    82  		Stop:      closeNotifier,
    83  		Version:   httputils.VersionFromContext(ctx),
    84  	}
    85  
    86  	return s.daemon.ContainerStats(vars["name"], config)
    87  }
    88  
    89  func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    90  	if err := httputils.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 := httputils.BoolValue(r, "stdout"), httputils.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  	containerName := vars["name"]
   122  
   123  	if !s.daemon.Exists(containerName) {
   124  		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
   125  	}
   126  
   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  	w.WriteHeader(http.StatusOK)
   131  	if flusher, ok := w.(http.Flusher); ok {
   132  		flusher.Flush()
   133  	}
   134  
   135  	output := ioutils.NewWriteFlusher(w)
   136  	defer output.Close()
   137  
   138  	logsConfig := &daemon.ContainerLogsConfig{
   139  		Follow:     httputils.BoolValue(r, "follow"),
   140  		Timestamps: httputils.BoolValue(r, "timestamps"),
   141  		Since:      since,
   142  		Tail:       r.Form.Get("tail"),
   143  		UseStdout:  stdout,
   144  		UseStderr:  stderr,
   145  		OutStream:  output,
   146  		Stop:       closeNotifier,
   147  	}
   148  
   149  	if err := s.daemon.ContainerLogs(containerName, logsConfig); err != nil {
   150  		// The client may be expecting all of the data we're sending to
   151  		// be multiplexed, so send it through OutStream, which will
   152  		// have been set up to handle that if needed.
   153  		fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err))
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  func (s *router) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   160  	if vars == nil {
   161  		return fmt.Errorf("Missing parameter")
   162  	}
   163  
   164  	return s.daemon.ContainerExport(vars["name"], w)
   165  }
   166  
   167  func (s *router) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   168  	if vars == nil {
   169  		return fmt.Errorf("Missing parameter")
   170  	}
   171  
   172  	// If contentLength is -1, we can assumed chunked encoding
   173  	// or more technically that the length is unknown
   174  	// https://golang.org/src/pkg/net/http/request.go#L139
   175  	// net/http otherwise seems to swallow any headers related to chunked encoding
   176  	// including r.TransferEncoding
   177  	// allow a nil body for backwards compatibility
   178  	var hostConfig *runconfig.HostConfig
   179  	if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
   180  		if err := httputils.CheckForJSON(r); err != nil {
   181  			return err
   182  		}
   183  
   184  		c, err := runconfig.DecodeHostConfig(r.Body)
   185  		if err != nil {
   186  			return err
   187  		}
   188  
   189  		hostConfig = c
   190  	}
   191  
   192  	if err := s.daemon.ContainerStart(vars["name"], hostConfig); err != nil {
   193  		return err
   194  	}
   195  	w.WriteHeader(http.StatusNoContent)
   196  	return nil
   197  }
   198  
   199  func (s *router) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   200  	if err := httputils.ParseForm(r); err != nil {
   201  		return err
   202  	}
   203  	if vars == nil {
   204  		return fmt.Errorf("Missing parameter")
   205  	}
   206  
   207  	seconds, _ := strconv.Atoi(r.Form.Get("t"))
   208  
   209  	if err := s.daemon.ContainerStop(vars["name"], seconds); err != nil {
   210  		return err
   211  	}
   212  	w.WriteHeader(http.StatusNoContent)
   213  
   214  	return nil
   215  }
   216  
   217  func (s *router) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   218  	if vars == nil {
   219  		return fmt.Errorf("Missing parameter")
   220  	}
   221  	if err := httputils.ParseForm(r); err != nil {
   222  		return err
   223  	}
   224  
   225  	var sig syscall.Signal
   226  	name := vars["name"]
   227  
   228  	// If we have a signal, look at it. Otherwise, do nothing
   229  	if sigStr := r.Form.Get("signal"); sigStr != "" {
   230  		var err error
   231  		if sig, err = signal.ParseSignal(sigStr); err != nil {
   232  			return err
   233  		}
   234  	}
   235  
   236  	if err := s.daemon.ContainerKill(name, uint64(sig)); err != nil {
   237  		theErr, isDerr := err.(errcode.ErrorCoder)
   238  		isStopped := isDerr && theErr.ErrorCode() == derr.ErrorCodeNotRunning
   239  
   240  		// Return error that's not caused because the container is stopped.
   241  		// Return error if the container is not running and the api is >= 1.20
   242  		// to keep backwards compatibility.
   243  		version := httputils.VersionFromContext(ctx)
   244  		if version.GreaterThanOrEqualTo("1.20") || !isStopped {
   245  			return fmt.Errorf("Cannot kill container %s: %v", name, err)
   246  		}
   247  	}
   248  
   249  	w.WriteHeader(http.StatusNoContent)
   250  	return nil
   251  }
   252  
   253  func (s *router) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   254  	if err := httputils.ParseForm(r); err != nil {
   255  		return err
   256  	}
   257  	if vars == nil {
   258  		return fmt.Errorf("Missing parameter")
   259  	}
   260  
   261  	timeout, _ := strconv.Atoi(r.Form.Get("t"))
   262  
   263  	if err := s.daemon.ContainerRestart(vars["name"], timeout); err != nil {
   264  		return err
   265  	}
   266  
   267  	w.WriteHeader(http.StatusNoContent)
   268  
   269  	return nil
   270  }
   271  
   272  func (s *router) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   273  	if vars == nil {
   274  		return fmt.Errorf("Missing parameter")
   275  	}
   276  	if err := httputils.ParseForm(r); err != nil {
   277  		return err
   278  	}
   279  
   280  	if err := s.daemon.ContainerPause(vars["name"]); err != nil {
   281  		return err
   282  	}
   283  
   284  	w.WriteHeader(http.StatusNoContent)
   285  
   286  	return nil
   287  }
   288  
   289  func (s *router) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   290  	if vars == nil {
   291  		return fmt.Errorf("Missing parameter")
   292  	}
   293  	if err := httputils.ParseForm(r); err != nil {
   294  		return err
   295  	}
   296  
   297  	if err := s.daemon.ContainerUnpause(vars["name"]); err != nil {
   298  		return err
   299  	}
   300  
   301  	w.WriteHeader(http.StatusNoContent)
   302  
   303  	return nil
   304  }
   305  
   306  func (s *router) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   307  	if vars == nil {
   308  		return fmt.Errorf("Missing parameter")
   309  	}
   310  
   311  	status, err := s.daemon.ContainerWait(vars["name"], -1*time.Second)
   312  	if err != nil {
   313  		return err
   314  	}
   315  
   316  	return httputils.WriteJSON(w, http.StatusOK, &types.ContainerWaitResponse{
   317  		StatusCode: status,
   318  	})
   319  }
   320  
   321  func (s *router) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   322  	if vars == nil {
   323  		return fmt.Errorf("Missing parameter")
   324  	}
   325  
   326  	changes, err := s.daemon.ContainerChanges(vars["name"])
   327  	if err != nil {
   328  		return err
   329  	}
   330  
   331  	return httputils.WriteJSON(w, http.StatusOK, changes)
   332  }
   333  
   334  func (s *router) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   335  	if vars == nil {
   336  		return fmt.Errorf("Missing parameter")
   337  	}
   338  
   339  	if err := httputils.ParseForm(r); err != nil {
   340  		return err
   341  	}
   342  
   343  	procList, err := s.daemon.ContainerTop(vars["name"], r.Form.Get("ps_args"))
   344  	if err != nil {
   345  		return err
   346  	}
   347  
   348  	return httputils.WriteJSON(w, http.StatusOK, procList)
   349  }
   350  
   351  func (s *router) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   352  	if err := httputils.ParseForm(r); err != nil {
   353  		return err
   354  	}
   355  	if vars == nil {
   356  		return fmt.Errorf("Missing parameter")
   357  	}
   358  
   359  	name := vars["name"]
   360  	newName := r.Form.Get("name")
   361  	if err := s.daemon.ContainerRename(name, newName); err != nil {
   362  		return err
   363  	}
   364  	w.WriteHeader(http.StatusNoContent)
   365  	return nil
   366  }
   367  
   368  func (s *router) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   369  	if err := httputils.ParseForm(r); err != nil {
   370  		return err
   371  	}
   372  	if err := httputils.CheckForJSON(r); err != nil {
   373  		return err
   374  	}
   375  
   376  	name := r.Form.Get("name")
   377  
   378  	config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
   379  	if err != nil {
   380  		return err
   381  	}
   382  	version := httputils.VersionFromContext(ctx)
   383  	adjustCPUShares := version.LessThan("1.19")
   384  
   385  	ccr, err := s.daemon.ContainerCreate(name, config, hostConfig, adjustCPUShares)
   386  	if err != nil {
   387  		return err
   388  	}
   389  
   390  	return httputils.WriteJSON(w, http.StatusCreated, ccr)
   391  }
   392  
   393  func (s *router) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   394  	if err := httputils.ParseForm(r); err != nil {
   395  		return err
   396  	}
   397  	if vars == nil {
   398  		return fmt.Errorf("Missing parameter")
   399  	}
   400  
   401  	name := vars["name"]
   402  	config := &daemon.ContainerRmConfig{
   403  		ForceRemove:  httputils.BoolValue(r, "force"),
   404  		RemoveVolume: httputils.BoolValue(r, "v"),
   405  		RemoveLink:   httputils.BoolValue(r, "link"),
   406  	}
   407  
   408  	if err := s.daemon.ContainerRm(name, config); err != nil {
   409  		// Force a 404 for the empty string
   410  		if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") {
   411  			return fmt.Errorf("no such id: \"\"")
   412  		}
   413  		return err
   414  	}
   415  
   416  	w.WriteHeader(http.StatusNoContent)
   417  
   418  	return nil
   419  }
   420  
   421  func (s *router) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   422  	if err := httputils.ParseForm(r); err != nil {
   423  		return err
   424  	}
   425  	if vars == nil {
   426  		return fmt.Errorf("Missing parameter")
   427  	}
   428  
   429  	height, err := strconv.Atoi(r.Form.Get("h"))
   430  	if err != nil {
   431  		return err
   432  	}
   433  	width, err := strconv.Atoi(r.Form.Get("w"))
   434  	if err != nil {
   435  		return err
   436  	}
   437  
   438  	return s.daemon.ContainerResize(vars["name"], height, width)
   439  }
   440  
   441  func (s *router) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   442  	if err := httputils.ParseForm(r); err != nil {
   443  		return err
   444  	}
   445  	if vars == nil {
   446  		return fmt.Errorf("Missing parameter")
   447  	}
   448  	containerName := vars["name"]
   449  
   450  	if !s.daemon.Exists(containerName) {
   451  		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
   452  	}
   453  
   454  	inStream, outStream, err := httputils.HijackConnection(w)
   455  	if err != nil {
   456  		return err
   457  	}
   458  	defer httputils.CloseStreams(inStream, outStream)
   459  
   460  	if _, ok := r.Header["Upgrade"]; ok {
   461  		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")
   462  	} else {
   463  		fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   464  	}
   465  
   466  	attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
   467  		InStream:  inStream,
   468  		OutStream: outStream,
   469  		UseStdin:  httputils.BoolValue(r, "stdin"),
   470  		UseStdout: httputils.BoolValue(r, "stdout"),
   471  		UseStderr: httputils.BoolValue(r, "stderr"),
   472  		Logs:      httputils.BoolValue(r, "logs"),
   473  		Stream:    httputils.BoolValue(r, "stream"),
   474  	}
   475  
   476  	if err := s.daemon.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil {
   477  		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
   478  	}
   479  
   480  	return nil
   481  }
   482  
   483  func (s *router) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   484  	if err := httputils.ParseForm(r); err != nil {
   485  		return err
   486  	}
   487  	if vars == nil {
   488  		return fmt.Errorf("Missing parameter")
   489  	}
   490  	containerName := vars["name"]
   491  
   492  	if !s.daemon.Exists(containerName) {
   493  		return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
   494  	}
   495  
   496  	h := websocket.Handler(func(ws *websocket.Conn) {
   497  		defer ws.Close()
   498  
   499  		wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{
   500  			InStream:  ws,
   501  			OutStream: ws,
   502  			ErrStream: ws,
   503  			Logs:      httputils.BoolValue(r, "logs"),
   504  			Stream:    httputils.BoolValue(r, "stream"),
   505  		}
   506  
   507  		if err := s.daemon.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {
   508  			logrus.Errorf("Error attaching websocket: %s", err)
   509  		}
   510  	})
   511  	ws := websocket.Server{Handler: h, Handshake: nil}
   512  	ws.ServeHTTP(w, r)
   513  
   514  	return nil
   515  }