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