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 }