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 }