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