github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/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 "strings" 10 "syscall" 11 "time" 12 13 "github.com/Sirupsen/logrus" 14 "github.com/docker/docker/api/server/httputils" 15 "github.com/docker/docker/api/types/backend" 16 "github.com/docker/docker/pkg/ioutils" 17 "github.com/docker/docker/pkg/signal" 18 "github.com/docker/engine-api/types" 19 "github.com/docker/engine-api/types/container" 20 "github.com/docker/engine-api/types/filters" 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 }, 102 OutStream: w, 103 } 104 105 chStarted := make(chan struct{}) 106 if err := s.backend.ContainerLogs(ctx, containerName, logsConfig, chStarted); err != nil { 107 select { 108 case <-chStarted: 109 // The client may be expecting all of the data we're sending to 110 // be multiplexed, so send it through OutStream, which will 111 // have been set up to handle that if needed. 112 fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %v\n", err) 113 default: 114 return err 115 } 116 } 117 118 return nil 119 } 120 121 func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 122 return s.backend.ContainerExport(vars["name"], w) 123 } 124 125 func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 126 // If contentLength is -1, we can assumed chunked encoding 127 // or more technically that the length is unknown 128 // https://golang.org/src/pkg/net/http/request.go#L139 129 // net/http otherwise seems to swallow any headers related to chunked encoding 130 // including r.TransferEncoding 131 // allow a nil body for backwards compatibility 132 var hostConfig *container.HostConfig 133 if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) { 134 if err := httputils.CheckForJSON(r); err != nil { 135 return err 136 } 137 138 c, err := s.decoder.DecodeHostConfig(r.Body) 139 if err != nil { 140 return err 141 } 142 143 hostConfig = c 144 } 145 146 if err := s.backend.ContainerStart(vars["name"], hostConfig); err != nil { 147 return err 148 } 149 w.WriteHeader(http.StatusNoContent) 150 return nil 151 } 152 153 func (s *containerRouter) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 154 if err := httputils.ParseForm(r); err != nil { 155 return err 156 } 157 158 seconds, _ := strconv.Atoi(r.Form.Get("t")) 159 160 if err := s.backend.ContainerStop(vars["name"], seconds); err != nil { 161 return err 162 } 163 w.WriteHeader(http.StatusNoContent) 164 165 return nil 166 } 167 168 type errContainerIsRunning interface { 169 ContainerIsRunning() bool 170 } 171 172 func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 173 if err := httputils.ParseForm(r); err != nil { 174 return err 175 } 176 177 var sig syscall.Signal 178 name := vars["name"] 179 180 // If we have a signal, look at it. Otherwise, do nothing 181 if sigStr := r.Form.Get("signal"); sigStr != "" { 182 var err error 183 if sig, err = signal.ParseSignal(sigStr); err != nil { 184 return err 185 } 186 } 187 188 if err := s.backend.ContainerKill(name, uint64(sig)); err != nil { 189 var isStopped bool 190 if e, ok := err.(errContainerIsRunning); ok { 191 isStopped = !e.ContainerIsRunning() 192 } 193 194 // Return error that's not caused because the container is stopped. 195 // Return error if the container is not running and the api is >= 1.20 196 // to keep backwards compatibility. 197 version := httputils.VersionFromContext(ctx) 198 if version.GreaterThanOrEqualTo("1.20") || !isStopped { 199 return fmt.Errorf("Cannot kill container %s: %v", name, err) 200 } 201 } 202 203 w.WriteHeader(http.StatusNoContent) 204 return nil 205 } 206 207 func (s *containerRouter) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 208 if err := httputils.ParseForm(r); err != nil { 209 return err 210 } 211 212 timeout, _ := strconv.Atoi(r.Form.Get("t")) 213 214 if err := s.backend.ContainerRestart(vars["name"], timeout); err != nil { 215 return err 216 } 217 218 w.WriteHeader(http.StatusNoContent) 219 220 return nil 221 } 222 223 func (s *containerRouter) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 224 if err := httputils.ParseForm(r); err != nil { 225 return err 226 } 227 228 if err := s.backend.ContainerPause(vars["name"]); err != nil { 229 return err 230 } 231 232 w.WriteHeader(http.StatusNoContent) 233 234 return nil 235 } 236 237 func (s *containerRouter) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 238 if err := httputils.ParseForm(r); err != nil { 239 return err 240 } 241 242 if err := s.backend.ContainerUnpause(vars["name"]); err != nil { 243 return err 244 } 245 246 w.WriteHeader(http.StatusNoContent) 247 248 return nil 249 } 250 251 func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 252 status, err := s.backend.ContainerWait(vars["name"], -1*time.Second) 253 if err != nil { 254 return err 255 } 256 257 return httputils.WriteJSON(w, http.StatusOK, &types.ContainerWaitResponse{ 258 StatusCode: status, 259 }) 260 } 261 262 func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 263 changes, err := s.backend.ContainerChanges(vars["name"]) 264 if err != nil { 265 return err 266 } 267 268 return httputils.WriteJSON(w, http.StatusOK, changes) 269 } 270 271 func (s *containerRouter) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 272 if err := httputils.ParseForm(r); err != nil { 273 return err 274 } 275 276 procList, err := s.backend.ContainerTop(vars["name"], r.Form.Get("ps_args")) 277 if err != nil { 278 return err 279 } 280 281 return httputils.WriteJSON(w, http.StatusOK, procList) 282 } 283 284 func (s *containerRouter) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 285 if err := httputils.ParseForm(r); err != nil { 286 return err 287 } 288 289 name := vars["name"] 290 newName := r.Form.Get("name") 291 if err := s.backend.ContainerRename(name, newName); err != nil { 292 return err 293 } 294 w.WriteHeader(http.StatusNoContent) 295 return nil 296 } 297 298 func (s *containerRouter) postContainerUpdate(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 if err := httputils.CheckForJSON(r); err != nil { 303 return err 304 } 305 306 var updateConfig container.UpdateConfig 307 308 decoder := json.NewDecoder(r.Body) 309 if err := decoder.Decode(&updateConfig); err != nil { 310 return err 311 } 312 313 hostConfig := &container.HostConfig{ 314 Resources: updateConfig.Resources, 315 RestartPolicy: updateConfig.RestartPolicy, 316 } 317 318 name := vars["name"] 319 warnings, err := s.backend.ContainerUpdate(name, hostConfig) 320 if err != nil { 321 return err 322 } 323 324 return httputils.WriteJSON(w, http.StatusOK, &types.ContainerUpdateResponse{ 325 Warnings: warnings, 326 }) 327 } 328 329 func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 330 if err := httputils.ParseForm(r); err != nil { 331 return err 332 } 333 if err := httputils.CheckForJSON(r); err != nil { 334 return err 335 } 336 337 name := r.Form.Get("name") 338 339 config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body) 340 if err != nil { 341 return err 342 } 343 version := httputils.VersionFromContext(ctx) 344 adjustCPUShares := version.LessThan("1.19") 345 346 ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{ 347 Name: name, 348 Config: config, 349 HostConfig: hostConfig, 350 NetworkingConfig: networkingConfig, 351 AdjustCPUShares: adjustCPUShares, 352 }) 353 if err != nil { 354 return err 355 } 356 357 return httputils.WriteJSON(w, http.StatusCreated, ccr) 358 } 359 360 func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 361 if err := httputils.ParseForm(r); err != nil { 362 return err 363 } 364 365 name := vars["name"] 366 config := &types.ContainerRmConfig{ 367 ForceRemove: httputils.BoolValue(r, "force"), 368 RemoveVolume: httputils.BoolValue(r, "v"), 369 RemoveLink: httputils.BoolValue(r, "link"), 370 } 371 372 if err := s.backend.ContainerRm(name, config); err != nil { 373 // Force a 404 for the empty string 374 if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") { 375 return fmt.Errorf("no such container: \"\"") 376 } 377 return err 378 } 379 380 w.WriteHeader(http.StatusNoContent) 381 382 return nil 383 } 384 385 func (s *containerRouter) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 386 if err := httputils.ParseForm(r); err != nil { 387 return err 388 } 389 390 height, err := strconv.Atoi(r.Form.Get("h")) 391 if err != nil { 392 return err 393 } 394 width, err := strconv.Atoi(r.Form.Get("w")) 395 if err != nil { 396 return err 397 } 398 399 return s.backend.ContainerResize(vars["name"], height, width) 400 } 401 402 func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 403 err := httputils.ParseForm(r) 404 if err != nil { 405 return err 406 } 407 containerName := vars["name"] 408 409 _, upgrade := r.Header["Upgrade"] 410 detachKeys := r.FormValue("detachKeys") 411 412 hijacker, ok := w.(http.Hijacker) 413 if !ok { 414 return fmt.Errorf("error attaching to container %s, hijack connection missing", containerName) 415 } 416 417 setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) { 418 conn, _, err := hijacker.Hijack() 419 if err != nil { 420 return nil, nil, nil, err 421 } 422 423 // set raw mode 424 conn.Write([]byte{}) 425 426 if upgrade { 427 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") 428 } else { 429 fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") 430 } 431 432 closer := func() error { 433 httputils.CloseStreams(conn) 434 return nil 435 } 436 return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil 437 } 438 439 attachConfig := &backend.ContainerAttachConfig{ 440 GetStreams: setupStreams, 441 UseStdin: httputils.BoolValue(r, "stdin"), 442 UseStdout: httputils.BoolValue(r, "stdout"), 443 UseStderr: httputils.BoolValue(r, "stderr"), 444 Logs: httputils.BoolValue(r, "logs"), 445 Stream: httputils.BoolValue(r, "stream"), 446 DetachKeys: detachKeys, 447 MuxStreams: true, 448 } 449 450 if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil { 451 logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err) 452 // Remember to close stream if error happens 453 conn, _, errHijack := hijacker.Hijack() 454 if errHijack == nil { 455 statusCode := httputils.GetHTTPErrorStatusCode(err) 456 statusText := http.StatusText(statusCode) 457 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()) 458 httputils.CloseStreams(conn) 459 } else { 460 logrus.Errorf("Error Hijacking: %v", err) 461 } 462 } 463 return nil 464 } 465 466 func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 467 if err := httputils.ParseForm(r); err != nil { 468 return err 469 } 470 containerName := vars["name"] 471 472 var err error 473 detachKeys := r.FormValue("detachKeys") 474 475 done := make(chan struct{}) 476 started := make(chan struct{}) 477 478 setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) { 479 wsChan := make(chan *websocket.Conn) 480 h := func(conn *websocket.Conn) { 481 wsChan <- conn 482 <-done 483 } 484 485 srv := websocket.Server{Handler: h, Handshake: nil} 486 go func() { 487 close(started) 488 srv.ServeHTTP(w, r) 489 }() 490 491 conn := <-wsChan 492 return conn, conn, conn, nil 493 } 494 495 attachConfig := &backend.ContainerAttachConfig{ 496 GetStreams: setupStreams, 497 Logs: httputils.BoolValue(r, "logs"), 498 Stream: httputils.BoolValue(r, "stream"), 499 DetachKeys: detachKeys, 500 UseStdin: true, 501 UseStdout: true, 502 UseStderr: true, 503 MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr 504 } 505 506 err = s.backend.ContainerAttach(containerName, attachConfig) 507 close(done) 508 select { 509 case <-started: 510 logrus.Errorf("Error attaching websocket: %s", err) 511 return nil 512 default: 513 } 514 return err 515 }