github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/api/server/router/container/container_routes.go (about) 1 package container 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net/http" 8 "strconv" 9 "syscall" 10 "time" 11 12 "github.com/Sirupsen/logrus" 13 "github.com/docker/docker/api/server/httputils" 14 "github.com/docker/docker/api/types" 15 "github.com/docker/docker/api/types/backend" 16 "github.com/docker/docker/api/types/container" 17 "github.com/docker/docker/api/types/filters" 18 "github.com/docker/docker/api/types/versions" 19 "github.com/docker/docker/pkg/ioutils" 20 "github.com/docker/docker/pkg/signal" 21 "golang.org/x/net/context" 22 "golang.org/x/net/websocket" 23 ) 24 25 func (s *containerRouter) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 26 if err := httputils.ParseForm(r); err != nil { 27 return err 28 } 29 filter, err := filters.FromParam(r.Form.Get("filters")) 30 if err != nil { 31 return err 32 } 33 34 config := &types.ContainerListOptions{ 35 All: httputils.BoolValue(r, "all"), 36 Size: httputils.BoolValue(r, "size"), 37 Since: r.Form.Get("since"), 38 Before: r.Form.Get("before"), 39 Filter: filter, 40 } 41 42 if tmpLimit := r.Form.Get("limit"); tmpLimit != "" { 43 limit, err := strconv.Atoi(tmpLimit) 44 if err != nil { 45 return err 46 } 47 config.Limit = limit 48 } 49 50 containers, err := s.backend.Containers(config) 51 if err != nil { 52 return err 53 } 54 55 return httputils.WriteJSON(w, http.StatusOK, containers) 56 } 57 58 func (s *containerRouter) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 59 if err := httputils.ParseForm(r); err != nil { 60 return err 61 } 62 63 stream := httputils.BoolValueOrDefault(r, "stream", true) 64 if !stream { 65 w.Header().Set("Content-Type", "application/json") 66 } 67 68 config := &backend.ContainerStatsConfig{ 69 Stream: stream, 70 OutStream: w, 71 Version: string(httputils.VersionFromContext(ctx)), 72 } 73 74 return s.backend.ContainerStats(ctx, vars["name"], config) 75 } 76 77 func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 78 if err := httputils.ParseForm(r); err != nil { 79 return err 80 } 81 82 // Args are validated before the stream starts because when it starts we're 83 // sending HTTP 200 by writing an empty chunk of data to tell the client that 84 // daemon is going to stream. By sending this initial HTTP 200 we can't report 85 // any error after the stream starts (i.e. container not found, wrong parameters) 86 // with the appropriate status code. 87 stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr") 88 if !(stdout || stderr) { 89 return fmt.Errorf("Bad parameters: you must choose at least one stream") 90 } 91 92 containerName := vars["name"] 93 logsConfig := &backend.ContainerLogsConfig{ 94 ContainerLogsOptions: types.ContainerLogsOptions{ 95 Follow: httputils.BoolValue(r, "follow"), 96 Timestamps: httputils.BoolValue(r, "timestamps"), 97 Since: r.Form.Get("since"), 98 Tail: r.Form.Get("tail"), 99 ShowStdout: stdout, 100 ShowStderr: stderr, 101 Details: httputils.BoolValue(r, "details"), 102 }, 103 OutStream: w, 104 } 105 106 chStarted := make(chan struct{}) 107 if err := s.backend.ContainerLogs(ctx, containerName, logsConfig, chStarted); err != nil { 108 select { 109 case <-chStarted: 110 // The client may be expecting all of the data we're sending to 111 // be multiplexed, so send it through OutStream, which will 112 // have been set up to handle that if needed. 113 fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %v\n", err) 114 default: 115 return err 116 } 117 } 118 119 return nil 120 } 121 122 func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 123 return s.backend.ContainerExport(vars["name"], w) 124 } 125 126 func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 127 // If contentLength is -1, we can assumed chunked encoding 128 // or more technically that the length is unknown 129 // https://golang.org/src/pkg/net/http/request.go#L139 130 // net/http otherwise seems to swallow any headers related to chunked encoding 131 // including r.TransferEncoding 132 // allow a nil body for backwards compatibility 133 134 version := httputils.VersionFromContext(ctx) 135 var hostConfig *container.HostConfig 136 // A non-nil json object is at least 7 characters. 137 if r.ContentLength > 7 || r.ContentLength == -1 { 138 if versions.GreaterThanOrEqualTo(version, "1.24") { 139 return validationError{fmt.Errorf("starting container with non-empty request body was deprecated since v1.10 and removed in v1.12")} 140 } 141 142 if err := httputils.CheckForJSON(r); err != nil { 143 return err 144 } 145 146 c, err := s.decoder.DecodeHostConfig(r.Body) 147 if err != nil { 148 return err 149 } 150 hostConfig = c 151 } 152 153 if err := httputils.ParseForm(r); err != nil { 154 return err 155 } 156 157 checkpoint := r.Form.Get("checkpoint") 158 validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") 159 if err := s.backend.ContainerStart(vars["name"], hostConfig, validateHostname, checkpoint); err != nil { 160 return err 161 } 162 163 w.WriteHeader(http.StatusNoContent) 164 return nil 165 } 166 167 func (s *containerRouter) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 168 if err := httputils.ParseForm(r); err != nil { 169 return err 170 } 171 172 var seconds *int 173 if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" { 174 valSeconds, err := strconv.Atoi(tmpSeconds) 175 if err != nil { 176 return err 177 } 178 seconds = &valSeconds 179 } 180 181 if err := s.backend.ContainerStop(vars["name"], seconds); err != nil { 182 return err 183 } 184 w.WriteHeader(http.StatusNoContent) 185 186 return nil 187 } 188 189 type errContainerIsRunning interface { 190 ContainerIsRunning() bool 191 } 192 193 func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 194 if err := httputils.ParseForm(r); err != nil { 195 return err 196 } 197 198 var sig syscall.Signal 199 name := vars["name"] 200 201 // If we have a signal, look at it. Otherwise, do nothing 202 if sigStr := r.Form.Get("signal"); sigStr != "" { 203 var err error 204 if sig, err = signal.ParseSignal(sigStr); err != nil { 205 return err 206 } 207 } 208 209 if err := s.backend.ContainerKill(name, uint64(sig)); err != nil { 210 var isStopped bool 211 if e, ok := err.(errContainerIsRunning); ok { 212 isStopped = !e.ContainerIsRunning() 213 } 214 215 // Return error that's not caused because the container is stopped. 216 // Return error if the container is not running and the api is >= 1.20 217 // to keep backwards compatibility. 218 version := httputils.VersionFromContext(ctx) 219 if versions.GreaterThanOrEqualTo(version, "1.20") || !isStopped { 220 return fmt.Errorf("Cannot kill container %s: %v", name, err) 221 } 222 } 223 224 w.WriteHeader(http.StatusNoContent) 225 return nil 226 } 227 228 func (s *containerRouter) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 229 if err := httputils.ParseForm(r); err != nil { 230 return err 231 } 232 233 var seconds *int 234 if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" { 235 valSeconds, err := strconv.Atoi(tmpSeconds) 236 if err != nil { 237 return err 238 } 239 seconds = &valSeconds 240 } 241 242 if err := s.backend.ContainerRestart(vars["name"], seconds); err != nil { 243 return err 244 } 245 246 w.WriteHeader(http.StatusNoContent) 247 248 return nil 249 } 250 251 func (s *containerRouter) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 252 if err := httputils.ParseForm(r); err != nil { 253 return err 254 } 255 256 if err := s.backend.ContainerPause(vars["name"]); err != nil { 257 return err 258 } 259 260 w.WriteHeader(http.StatusNoContent) 261 262 return nil 263 } 264 265 func (s *containerRouter) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 266 if err := httputils.ParseForm(r); err != nil { 267 return err 268 } 269 270 if err := s.backend.ContainerUnpause(vars["name"]); err != nil { 271 return err 272 } 273 274 w.WriteHeader(http.StatusNoContent) 275 276 return nil 277 } 278 279 func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 280 status, err := s.backend.ContainerWait(vars["name"], -1*time.Second) 281 if err != nil { 282 return err 283 } 284 285 return httputils.WriteJSON(w, http.StatusOK, &types.ContainerWaitResponse{ 286 StatusCode: status, 287 }) 288 } 289 290 func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 291 changes, err := s.backend.ContainerChanges(vars["name"]) 292 if err != nil { 293 return err 294 } 295 296 return httputils.WriteJSON(w, http.StatusOK, changes) 297 } 298 299 func (s *containerRouter) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 300 if err := httputils.ParseForm(r); err != nil { 301 return err 302 } 303 304 procList, err := s.backend.ContainerTop(vars["name"], r.Form.Get("ps_args")) 305 if err != nil { 306 return err 307 } 308 309 return httputils.WriteJSON(w, http.StatusOK, procList) 310 } 311 312 func (s *containerRouter) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 313 if err := httputils.ParseForm(r); err != nil { 314 return err 315 } 316 317 name := vars["name"] 318 newName := r.Form.Get("name") 319 if err := s.backend.ContainerRename(name, newName); err != nil { 320 return err 321 } 322 w.WriteHeader(http.StatusNoContent) 323 return nil 324 } 325 326 func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 327 if err := httputils.ParseForm(r); err != nil { 328 return err 329 } 330 if err := httputils.CheckForJSON(r); err != nil { 331 return err 332 } 333 334 version := httputils.VersionFromContext(ctx) 335 var updateConfig container.UpdateConfig 336 337 decoder := json.NewDecoder(r.Body) 338 if err := decoder.Decode(&updateConfig); err != nil { 339 return err 340 } 341 342 hostConfig := &container.HostConfig{ 343 Resources: updateConfig.Resources, 344 RestartPolicy: updateConfig.RestartPolicy, 345 } 346 347 name := vars["name"] 348 validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") 349 resp, err := s.backend.ContainerUpdate(name, hostConfig, validateHostname) 350 if err != nil { 351 return err 352 } 353 354 return httputils.WriteJSON(w, http.StatusOK, resp) 355 } 356 357 func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 358 if err := httputils.ParseForm(r); err != nil { 359 return err 360 } 361 if err := httputils.CheckForJSON(r); err != nil { 362 return err 363 } 364 365 name := r.Form.Get("name") 366 367 config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body) 368 if err != nil { 369 return err 370 } 371 version := httputils.VersionFromContext(ctx) 372 adjustCPUShares := versions.LessThan(version, "1.19") 373 374 validateHostname := versions.GreaterThanOrEqualTo(version, "1.24") 375 ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{ 376 Name: name, 377 Config: config, 378 HostConfig: hostConfig, 379 NetworkingConfig: networkingConfig, 380 AdjustCPUShares: adjustCPUShares, 381 }, validateHostname) 382 if err != nil { 383 return err 384 } 385 386 return httputils.WriteJSON(w, http.StatusCreated, ccr) 387 } 388 389 func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 390 if err := httputils.ParseForm(r); err != nil { 391 return err 392 } 393 394 name := vars["name"] 395 config := &types.ContainerRmConfig{ 396 ForceRemove: httputils.BoolValue(r, "force"), 397 RemoveVolume: httputils.BoolValue(r, "v"), 398 RemoveLink: httputils.BoolValue(r, "link"), 399 } 400 401 if err := s.backend.ContainerRm(name, config); err != nil { 402 return err 403 } 404 405 w.WriteHeader(http.StatusNoContent) 406 407 return nil 408 } 409 410 func (s *containerRouter) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 411 if err := httputils.ParseForm(r); err != nil { 412 return err 413 } 414 415 height, err := strconv.Atoi(r.Form.Get("h")) 416 if err != nil { 417 return err 418 } 419 width, err := strconv.Atoi(r.Form.Get("w")) 420 if err != nil { 421 return err 422 } 423 424 return s.backend.ContainerResize(vars["name"], height, width) 425 } 426 427 func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 428 err := httputils.ParseForm(r) 429 if err != nil { 430 return err 431 } 432 containerName := vars["name"] 433 434 _, upgrade := r.Header["Upgrade"] 435 detachKeys := r.FormValue("detachKeys") 436 437 hijacker, ok := w.(http.Hijacker) 438 if !ok { 439 return fmt.Errorf("error attaching to container %s, hijack connection missing", containerName) 440 } 441 442 setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) { 443 conn, _, err := hijacker.Hijack() 444 if err != nil { 445 return nil, nil, nil, err 446 } 447 448 // set raw mode 449 conn.Write([]byte{}) 450 451 if upgrade { 452 fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") 453 } else { 454 fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") 455 } 456 457 closer := func() error { 458 httputils.CloseStreams(conn) 459 return nil 460 } 461 return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil 462 } 463 464 attachConfig := &backend.ContainerAttachConfig{ 465 GetStreams: setupStreams, 466 UseStdin: httputils.BoolValue(r, "stdin"), 467 UseStdout: httputils.BoolValue(r, "stdout"), 468 UseStderr: httputils.BoolValue(r, "stderr"), 469 Logs: httputils.BoolValue(r, "logs"), 470 Stream: httputils.BoolValue(r, "stream"), 471 DetachKeys: detachKeys, 472 MuxStreams: true, 473 } 474 475 if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil { 476 logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err) 477 // Remember to close stream if error happens 478 conn, _, errHijack := hijacker.Hijack() 479 if errHijack == nil { 480 statusCode := httputils.GetHTTPErrorStatusCode(err) 481 statusText := http.StatusText(statusCode) 482 fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n%s\r\n", statusCode, statusText, err.Error()) 483 httputils.CloseStreams(conn) 484 } else { 485 logrus.Errorf("Error Hijacking: %v", err) 486 } 487 } 488 return nil 489 } 490 491 func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 492 if err := httputils.ParseForm(r); err != nil { 493 return err 494 } 495 containerName := vars["name"] 496 497 var err error 498 detachKeys := r.FormValue("detachKeys") 499 500 done := make(chan struct{}) 501 started := make(chan struct{}) 502 503 setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) { 504 wsChan := make(chan *websocket.Conn) 505 h := func(conn *websocket.Conn) { 506 wsChan <- conn 507 <-done 508 } 509 510 srv := websocket.Server{Handler: h, Handshake: nil} 511 go func() { 512 close(started) 513 srv.ServeHTTP(w, r) 514 }() 515 516 conn := <-wsChan 517 return conn, conn, conn, nil 518 } 519 520 attachConfig := &backend.ContainerAttachConfig{ 521 GetStreams: setupStreams, 522 Logs: httputils.BoolValue(r, "logs"), 523 Stream: httputils.BoolValue(r, "stream"), 524 DetachKeys: detachKeys, 525 UseStdin: true, 526 UseStdout: true, 527 UseStderr: true, 528 MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr 529 } 530 531 err = s.backend.ContainerAttach(containerName, attachConfig) 532 close(done) 533 select { 534 case <-started: 535 logrus.Errorf("Error attaching websocket: %s", err) 536 return nil 537 default: 538 } 539 return err 540 } 541 542 func (s *containerRouter) postContainersPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 543 if err := httputils.ParseForm(r); err != nil { 544 return err 545 } 546 547 if err := httputils.CheckForJSON(r); err != nil { 548 return err 549 } 550 551 var cfg types.ContainersPruneConfig 552 if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { 553 return err 554 } 555 556 pruneReport, err := s.backend.ContainersPrune(&cfg) 557 if err != nil { 558 return err 559 } 560 return httputils.WriteJSON(w, http.StatusOK, pruneReport) 561 }