github.com/rsampaio/docker@v0.7.2-0.20150827203920-fdc73cc3fc31/api/server/container.go (about)

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