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