github.com/campoy/docker@v1.8.0-rc1/api/server/server.go (about) 1 package server 2 3 import ( 4 "crypto/tls" 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "io" 9 "net" 10 "net/http" 11 "os" 12 "runtime" 13 "strconv" 14 "strings" 15 "time" 16 17 "github.com/gorilla/mux" 18 "golang.org/x/net/websocket" 19 20 "github.com/Sirupsen/logrus" 21 "github.com/docker/docker/api" 22 "github.com/docker/docker/api/types" 23 "github.com/docker/docker/autogen/dockerversion" 24 "github.com/docker/docker/builder" 25 "github.com/docker/docker/cliconfig" 26 "github.com/docker/docker/daemon" 27 "github.com/docker/docker/graph" 28 "github.com/docker/docker/pkg/ioutils" 29 "github.com/docker/docker/pkg/jsonmessage" 30 "github.com/docker/docker/pkg/parsers" 31 "github.com/docker/docker/pkg/parsers/filters" 32 "github.com/docker/docker/pkg/parsers/kernel" 33 "github.com/docker/docker/pkg/signal" 34 "github.com/docker/docker/pkg/sockets" 35 "github.com/docker/docker/pkg/stdcopy" 36 "github.com/docker/docker/pkg/streamformatter" 37 "github.com/docker/docker/pkg/ulimit" 38 "github.com/docker/docker/pkg/version" 39 "github.com/docker/docker/runconfig" 40 "github.com/docker/docker/utils" 41 ) 42 43 type ServerConfig struct { 44 Logging bool 45 EnableCors bool 46 CorsHeaders string 47 Version string 48 SocketGroup string 49 TLSConfig *tls.Config 50 } 51 52 type Server struct { 53 daemon *daemon.Daemon 54 cfg *ServerConfig 55 router *mux.Router 56 start chan struct{} 57 servers []serverCloser 58 } 59 60 func New(cfg *ServerConfig) *Server { 61 srv := &Server{ 62 cfg: cfg, 63 start: make(chan struct{}), 64 } 65 r := createRouter(srv) 66 srv.router = r 67 return srv 68 } 69 70 func (s *Server) Close() { 71 for _, srv := range s.servers { 72 if err := srv.Close(); err != nil { 73 logrus.Error(err) 74 } 75 } 76 } 77 78 type serverCloser interface { 79 Serve() error 80 Close() error 81 } 82 83 // ServeApi loops through all of the protocols sent in to docker and spawns 84 // off a go routine to setup a serving http.Server for each. 85 func (s *Server) ServeApi(protoAddrs []string) error { 86 var chErrors = make(chan error, len(protoAddrs)) 87 88 for _, protoAddr := range protoAddrs { 89 protoAddrParts := strings.SplitN(protoAddr, "://", 2) 90 if len(protoAddrParts) != 2 { 91 return fmt.Errorf("bad format, expected PROTO://ADDR") 92 } 93 srv, err := s.newServer(protoAddrParts[0], protoAddrParts[1]) 94 if err != nil { 95 return err 96 } 97 s.servers = append(s.servers, srv...) 98 99 for _, s := range srv { 100 logrus.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) 101 go func(s serverCloser) { 102 if err := s.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") { 103 err = nil 104 } 105 chErrors <- err 106 }(s) 107 } 108 } 109 110 for i := 0; i < len(protoAddrs); i++ { 111 err := <-chErrors 112 if err != nil { 113 return err 114 } 115 } 116 117 return nil 118 } 119 120 type HttpServer struct { 121 srv *http.Server 122 l net.Listener 123 } 124 125 func (s *HttpServer) Serve() error { 126 return s.srv.Serve(s.l) 127 } 128 func (s *HttpServer) Close() error { 129 return s.l.Close() 130 } 131 132 type HttpApiFunc func(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error 133 134 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { 135 conn, _, err := w.(http.Hijacker).Hijack() 136 if err != nil { 137 return nil, nil, err 138 } 139 // Flush the options to make sure the client sets the raw mode 140 conn.Write([]byte{}) 141 return conn, conn, nil 142 } 143 144 func closeStreams(streams ...interface{}) { 145 for _, stream := range streams { 146 if tcpc, ok := stream.(interface { 147 CloseWrite() error 148 }); ok { 149 tcpc.CloseWrite() 150 } else if closer, ok := stream.(io.Closer); ok { 151 closer.Close() 152 } 153 } 154 } 155 156 // Check to make sure request's Content-Type is application/json 157 func checkForJson(r *http.Request) error { 158 ct := r.Header.Get("Content-Type") 159 160 // No Content-Type header is ok as long as there's no Body 161 if ct == "" { 162 if r.Body == nil || r.ContentLength == 0 { 163 return nil 164 } 165 } 166 167 // Otherwise it better be json 168 if api.MatchesContentType(ct, "application/json") { 169 return nil 170 } 171 return fmt.Errorf("Content-Type specified (%s) must be 'application/json'", ct) 172 } 173 174 //If we don't do this, POST method without Content-type (even with empty body) will fail 175 func parseForm(r *http.Request) error { 176 if r == nil { 177 return nil 178 } 179 if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") { 180 return err 181 } 182 return nil 183 } 184 185 func parseMultipartForm(r *http.Request) error { 186 if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") { 187 return err 188 } 189 return nil 190 } 191 192 func httpError(w http.ResponseWriter, err error) { 193 if err == nil || w == nil { 194 logrus.WithFields(logrus.Fields{"error": err, "writer": w}).Error("unexpected HTTP error handling") 195 return 196 } 197 statusCode := http.StatusInternalServerError 198 // FIXME: this is brittle and should not be necessary. 199 // If we need to differentiate between different possible error types, we should 200 // create appropriate error types with clearly defined meaning. 201 errStr := strings.ToLower(err.Error()) 202 for keyword, status := range map[string]int{ 203 "not found": http.StatusNotFound, 204 "no such": http.StatusNotFound, 205 "bad parameter": http.StatusBadRequest, 206 "conflict": http.StatusConflict, 207 "impossible": http.StatusNotAcceptable, 208 "wrong login/password": http.StatusUnauthorized, 209 "hasn't been activated": http.StatusForbidden, 210 } { 211 if strings.Contains(errStr, keyword) { 212 statusCode = status 213 break 214 } 215 } 216 217 logrus.WithFields(logrus.Fields{"statusCode": statusCode, "err": err}).Error("HTTP Error") 218 http.Error(w, err.Error(), statusCode) 219 } 220 221 // writeJSON writes the value v to the http response stream as json with standard 222 // json encoding. 223 func writeJSON(w http.ResponseWriter, code int, v interface{}) error { 224 w.Header().Set("Content-Type", "application/json") 225 w.WriteHeader(code) 226 return json.NewEncoder(w).Encode(v) 227 } 228 229 func (s *Server) postAuth(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 230 var config *cliconfig.AuthConfig 231 err := json.NewDecoder(r.Body).Decode(&config) 232 r.Body.Close() 233 if err != nil { 234 return err 235 } 236 status, err := s.daemon.RegistryService.Auth(config) 237 if err != nil { 238 return err 239 } 240 return writeJSON(w, http.StatusOK, &types.AuthResponse{ 241 Status: status, 242 }) 243 } 244 245 func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 246 v := &types.Version{ 247 Version: dockerversion.VERSION, 248 ApiVersion: api.Version, 249 GitCommit: dockerversion.GITCOMMIT, 250 GoVersion: runtime.Version(), 251 Os: runtime.GOOS, 252 Arch: runtime.GOARCH, 253 BuildTime: dockerversion.BUILDTIME, 254 } 255 256 if version.GreaterThanOrEqualTo("1.19") { 257 v.Experimental = utils.ExperimentalBuild() 258 } 259 260 if kernelVersion, err := kernel.GetKernelVersion(); err == nil { 261 v.KernelVersion = kernelVersion.String() 262 } 263 264 return writeJSON(w, http.StatusOK, v) 265 } 266 267 func (s *Server) postContainersKill(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 268 if vars == nil { 269 return fmt.Errorf("Missing parameter") 270 } 271 if err := parseForm(r); err != nil { 272 return err 273 } 274 275 var sig uint64 276 name := vars["name"] 277 278 // If we have a signal, look at it. Otherwise, do nothing 279 if sigStr := r.Form.Get("signal"); sigStr != "" { 280 // Check if we passed the signal as a number: 281 // The largest legal signal is 31, so let's parse on 5 bits 282 sigN, err := strconv.ParseUint(sigStr, 10, 5) 283 if err != nil { 284 // The signal is not a number, treat it as a string (either like 285 // "KILL" or like "SIGKILL") 286 syscallSig, ok := signal.SignalMap[strings.TrimPrefix(sigStr, "SIG")] 287 if !ok { 288 return fmt.Errorf("Invalid signal: %s", sigStr) 289 } 290 sig = uint64(syscallSig) 291 } else { 292 sig = sigN 293 } 294 295 if sig == 0 { 296 return fmt.Errorf("Invalid signal: %s", sigStr) 297 } 298 } 299 300 if err := s.daemon.ContainerKill(name, sig); err != nil { 301 return err 302 } 303 304 w.WriteHeader(http.StatusNoContent) 305 return nil 306 } 307 308 func (s *Server) postContainersPause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 309 if vars == nil { 310 return fmt.Errorf("Missing parameter") 311 } 312 if err := parseForm(r); err != nil { 313 return err 314 } 315 316 if err := s.daemon.ContainerPause(vars["name"]); err != nil { 317 return err 318 } 319 320 w.WriteHeader(http.StatusNoContent) 321 322 return nil 323 } 324 325 func (s *Server) postContainersUnpause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 326 if vars == nil { 327 return fmt.Errorf("Missing parameter") 328 } 329 if err := parseForm(r); err != nil { 330 return err 331 } 332 333 if err := s.daemon.ContainerUnpause(vars["name"]); err != nil { 334 return err 335 } 336 337 w.WriteHeader(http.StatusNoContent) 338 339 return nil 340 } 341 342 func (s *Server) getContainersExport(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 343 if vars == nil { 344 return fmt.Errorf("Missing parameter") 345 } 346 347 return s.daemon.ContainerExport(vars["name"], w) 348 } 349 350 func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 351 if err := parseForm(r); err != nil { 352 return err 353 } 354 355 imagesConfig := graph.ImagesConfig{ 356 Filters: r.Form.Get("filters"), 357 // FIXME this parameter could just be a match filter 358 Filter: r.Form.Get("filter"), 359 All: boolValue(r, "all"), 360 } 361 362 images, err := s.daemon.Repositories().Images(&imagesConfig) 363 if err != nil { 364 return err 365 } 366 367 return writeJSON(w, http.StatusOK, images) 368 } 369 370 func (s *Server) getInfo(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 371 info, err := s.daemon.SystemInfo() 372 if err != nil { 373 return err 374 } 375 376 return writeJSON(w, http.StatusOK, info) 377 } 378 379 func (s *Server) getEvents(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 380 if err := parseForm(r); err != nil { 381 return err 382 } 383 var since int64 = -1 384 if r.Form.Get("since") != "" { 385 s, err := strconv.ParseInt(r.Form.Get("since"), 10, 64) 386 if err != nil { 387 return err 388 } 389 since = s 390 } 391 392 var until int64 = -1 393 if r.Form.Get("until") != "" { 394 u, err := strconv.ParseInt(r.Form.Get("until"), 10, 64) 395 if err != nil { 396 return err 397 } 398 until = u 399 } 400 401 timer := time.NewTimer(0) 402 timer.Stop() 403 if until > 0 { 404 dur := time.Unix(until, 0).Sub(time.Now()) 405 timer = time.NewTimer(dur) 406 } 407 408 ef, err := filters.FromParam(r.Form.Get("filters")) 409 if err != nil { 410 return err 411 } 412 413 isFiltered := func(field string, filter []string) bool { 414 if len(field) == 0 { 415 return false 416 } 417 if len(filter) == 0 { 418 return false 419 } 420 for _, v := range filter { 421 if v == field { 422 return false 423 } 424 if strings.Contains(field, ":") { 425 image := strings.Split(field, ":") 426 if image[0] == v { 427 return false 428 } 429 } 430 } 431 return true 432 } 433 434 d := s.daemon 435 es := d.EventsService 436 w.Header().Set("Content-Type", "application/json") 437 outStream := ioutils.NewWriteFlusher(w) 438 outStream.Write(nil) // make sure response is sent immediately 439 enc := json.NewEncoder(outStream) 440 441 getContainerId := func(cn string) string { 442 c, err := d.Get(cn) 443 if err != nil { 444 return "" 445 } 446 return c.ID 447 } 448 449 sendEvent := func(ev *jsonmessage.JSONMessage) error { 450 //incoming container filter can be name,id or partial id, convert and replace as a full container id 451 for i, cn := range ef["container"] { 452 ef["container"][i] = getContainerId(cn) 453 } 454 455 if isFiltered(ev.Status, ef["event"]) || (isFiltered(ev.ID, ef["image"]) && 456 isFiltered(ev.From, ef["image"])) || isFiltered(ev.ID, ef["container"]) { 457 return nil 458 } 459 460 return enc.Encode(ev) 461 } 462 463 current, l := es.Subscribe() 464 if since == -1 { 465 current = nil 466 } 467 defer es.Evict(l) 468 for _, ev := range current { 469 if ev.Time < since { 470 continue 471 } 472 if err := sendEvent(ev); err != nil { 473 return err 474 } 475 } 476 477 var closeNotify <-chan bool 478 if closeNotifier, ok := w.(http.CloseNotifier); ok { 479 closeNotify = closeNotifier.CloseNotify() 480 } 481 482 for { 483 select { 484 case ev := <-l: 485 jev, ok := ev.(*jsonmessage.JSONMessage) 486 if !ok { 487 continue 488 } 489 if err := sendEvent(jev); err != nil { 490 return err 491 } 492 case <-timer.C: 493 return nil 494 case <-closeNotify: 495 logrus.Debug("Client disconnected, stop sending events") 496 return nil 497 } 498 } 499 } 500 501 func (s *Server) getImagesHistory(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 502 if vars == nil { 503 return fmt.Errorf("Missing parameter") 504 } 505 506 name := vars["name"] 507 history, err := s.daemon.Repositories().History(name) 508 if err != nil { 509 return err 510 } 511 512 return writeJSON(w, http.StatusOK, history) 513 } 514 515 func (s *Server) getContainersChanges(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 516 if vars == nil { 517 return fmt.Errorf("Missing parameter") 518 } 519 520 changes, err := s.daemon.ContainerChanges(vars["name"]) 521 if err != nil { 522 return err 523 } 524 525 return writeJSON(w, http.StatusOK, changes) 526 } 527 528 func (s *Server) getContainersTop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 529 if vars == nil { 530 return fmt.Errorf("Missing parameter") 531 } 532 533 if err := parseForm(r); err != nil { 534 return err 535 } 536 537 procList, err := s.daemon.ContainerTop(vars["name"], r.Form.Get("ps_args")) 538 if err != nil { 539 return err 540 } 541 542 return writeJSON(w, http.StatusOK, procList) 543 } 544 545 func (s *Server) getContainersJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 546 if err := parseForm(r); err != nil { 547 return err 548 } 549 550 config := &daemon.ContainersConfig{ 551 All: boolValue(r, "all"), 552 Size: boolValue(r, "size"), 553 Since: r.Form.Get("since"), 554 Before: r.Form.Get("before"), 555 Filters: r.Form.Get("filters"), 556 } 557 558 if tmpLimit := r.Form.Get("limit"); tmpLimit != "" { 559 limit, err := strconv.Atoi(tmpLimit) 560 if err != nil { 561 return err 562 } 563 config.Limit = limit 564 } 565 566 containers, err := s.daemon.Containers(config) 567 if err != nil { 568 return err 569 } 570 571 return writeJSON(w, http.StatusOK, containers) 572 } 573 574 func (s *Server) getContainersStats(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 575 if err := parseForm(r); err != nil { 576 return err 577 } 578 if vars == nil { 579 return fmt.Errorf("Missing parameter") 580 } 581 582 stream := boolValueOrDefault(r, "stream", true) 583 var out io.Writer 584 if !stream { 585 w.Header().Set("Content-Type", "application/json") 586 out = w 587 } else { 588 out = ioutils.NewWriteFlusher(w) 589 } 590 591 var closeNotifier <-chan bool 592 if notifier, ok := w.(http.CloseNotifier); ok { 593 closeNotifier = notifier.CloseNotify() 594 } 595 596 config := &daemon.ContainerStatsConfig{ 597 Stream: stream, 598 OutStream: out, 599 Stop: closeNotifier, 600 } 601 602 return s.daemon.ContainerStats(vars["name"], config) 603 } 604 605 func (s *Server) getContainersLogs(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 606 if err := parseForm(r); err != nil { 607 return err 608 } 609 if vars == nil { 610 return fmt.Errorf("Missing parameter") 611 } 612 613 // Validate args here, because we can't return not StatusOK after job.Run() call 614 stdout, stderr := boolValue(r, "stdout"), boolValue(r, "stderr") 615 if !(stdout || stderr) { 616 return fmt.Errorf("Bad parameters: you must choose at least one stream") 617 } 618 619 var since time.Time 620 if r.Form.Get("since") != "" { 621 s, err := strconv.ParseInt(r.Form.Get("since"), 10, 64) 622 if err != nil { 623 return err 624 } 625 since = time.Unix(s, 0) 626 } 627 628 var closeNotifier <-chan bool 629 if notifier, ok := w.(http.CloseNotifier); ok { 630 closeNotifier = notifier.CloseNotify() 631 } 632 633 c, err := s.daemon.Get(vars["name"]) 634 if err != nil { 635 return err 636 } 637 638 outStream := ioutils.NewWriteFlusher(w) 639 // write an empty chunk of data (this is to ensure that the 640 // HTTP Response is sent immediatly, even if the container has 641 // not yet produced any data) 642 outStream.Write(nil) 643 644 logsConfig := &daemon.ContainerLogsConfig{ 645 Follow: boolValue(r, "follow"), 646 Timestamps: boolValue(r, "timestamps"), 647 Since: since, 648 Tail: r.Form.Get("tail"), 649 UseStdout: stdout, 650 UseStderr: stderr, 651 OutStream: outStream, 652 Stop: closeNotifier, 653 } 654 655 if err := s.daemon.ContainerLogs(c, logsConfig); err != nil { 656 fmt.Fprintf(w, "Error running logs job: %s\n", err) 657 } 658 659 return nil 660 } 661 662 func (s *Server) postImagesTag(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 663 if err := parseForm(r); err != nil { 664 return err 665 } 666 if vars == nil { 667 return fmt.Errorf("Missing parameter") 668 } 669 670 repo := r.Form.Get("repo") 671 tag := r.Form.Get("tag") 672 force := boolValue(r, "force") 673 name := vars["name"] 674 if err := s.daemon.Repositories().Tag(repo, tag, name, force); err != nil { 675 return err 676 } 677 s.daemon.EventsService.Log("tag", utils.ImageReference(repo, tag), "") 678 w.WriteHeader(http.StatusCreated) 679 return nil 680 } 681 682 func (s *Server) postCommit(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 683 if err := parseForm(r); err != nil { 684 return err 685 } 686 687 if err := checkForJson(r); err != nil { 688 return err 689 } 690 691 cname := r.Form.Get("container") 692 693 pause := boolValue(r, "pause") 694 if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") { 695 pause = true 696 } 697 698 c, _, err := runconfig.DecodeContainerConfig(r.Body) 699 if err != nil && err != io.EOF { //Do not fail if body is empty. 700 return err 701 } 702 703 commitCfg := &builder.CommitConfig{ 704 Pause: pause, 705 Repo: r.Form.Get("repo"), 706 Tag: r.Form.Get("tag"), 707 Author: r.Form.Get("author"), 708 Comment: r.Form.Get("comment"), 709 Changes: r.Form["changes"], 710 Config: c, 711 } 712 713 imgID, err := builder.Commit(cname, s.daemon, commitCfg) 714 if err != nil { 715 return err 716 } 717 718 return writeJSON(w, http.StatusCreated, &types.ContainerCommitResponse{ 719 ID: imgID, 720 }) 721 } 722 723 // Creates an image from Pull or from Import 724 func (s *Server) postImagesCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 725 if err := parseForm(r); err != nil { 726 return err 727 } 728 729 var ( 730 image = r.Form.Get("fromImage") 731 repo = r.Form.Get("repo") 732 tag = r.Form.Get("tag") 733 ) 734 authEncoded := r.Header.Get("X-Registry-Auth") 735 authConfig := &cliconfig.AuthConfig{} 736 if authEncoded != "" { 737 authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 738 if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { 739 // for a pull it is not an error if no auth was given 740 // to increase compatibility with the existing api it is defaulting to be empty 741 authConfig = &cliconfig.AuthConfig{} 742 } 743 } 744 745 var ( 746 err error 747 output = ioutils.NewWriteFlusher(w) 748 ) 749 750 w.Header().Set("Content-Type", "application/json") 751 752 if image != "" { //pull 753 if tag == "" { 754 image, tag = parsers.ParseRepositoryTag(image) 755 } 756 metaHeaders := map[string][]string{} 757 for k, v := range r.Header { 758 if strings.HasPrefix(k, "X-Meta-") { 759 metaHeaders[k] = v 760 } 761 } 762 763 imagePullConfig := &graph.ImagePullConfig{ 764 MetaHeaders: metaHeaders, 765 AuthConfig: authConfig, 766 OutStream: output, 767 } 768 769 err = s.daemon.Repositories().Pull(image, tag, imagePullConfig) 770 } else { //import 771 if tag == "" { 772 repo, tag = parsers.ParseRepositoryTag(repo) 773 } 774 775 src := r.Form.Get("fromSrc") 776 imageImportConfig := &graph.ImageImportConfig{ 777 Changes: r.Form["changes"], 778 InConfig: r.Body, 779 OutStream: output, 780 } 781 782 // 'err' MUST NOT be defined within this block, we need any error 783 // generated from the download to be available to the output 784 // stream processing below 785 var newConfig *runconfig.Config 786 newConfig, err = builder.BuildFromConfig(s.daemon, &runconfig.Config{}, imageImportConfig.Changes) 787 if err != nil { 788 return err 789 } 790 imageImportConfig.ContainerConfig = newConfig 791 792 err = s.daemon.Repositories().Import(src, repo, tag, imageImportConfig) 793 } 794 if err != nil { 795 if !output.Flushed() { 796 return err 797 } 798 sf := streamformatter.NewJSONStreamFormatter() 799 output.Write(sf.FormatError(err)) 800 } 801 802 return nil 803 804 } 805 806 func (s *Server) getImagesSearch(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 807 if err := parseForm(r); err != nil { 808 return err 809 } 810 var ( 811 config *cliconfig.AuthConfig 812 authEncoded = r.Header.Get("X-Registry-Auth") 813 headers = map[string][]string{} 814 ) 815 816 if authEncoded != "" { 817 authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 818 if err := json.NewDecoder(authJson).Decode(&config); err != nil { 819 // for a search it is not an error if no auth was given 820 // to increase compatibility with the existing api it is defaulting to be empty 821 config = &cliconfig.AuthConfig{} 822 } 823 } 824 for k, v := range r.Header { 825 if strings.HasPrefix(k, "X-Meta-") { 826 headers[k] = v 827 } 828 } 829 query, err := s.daemon.RegistryService.Search(r.Form.Get("term"), config, headers) 830 if err != nil { 831 return err 832 } 833 return writeJSON(w, http.StatusOK, query.Results) 834 } 835 836 func (s *Server) postImagesPush(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 837 if vars == nil { 838 return fmt.Errorf("Missing parameter") 839 } 840 841 metaHeaders := map[string][]string{} 842 for k, v := range r.Header { 843 if strings.HasPrefix(k, "X-Meta-") { 844 metaHeaders[k] = v 845 } 846 } 847 if err := parseForm(r); err != nil { 848 return err 849 } 850 authConfig := &cliconfig.AuthConfig{} 851 852 authEncoded := r.Header.Get("X-Registry-Auth") 853 if authEncoded != "" { 854 // the new format is to handle the authConfig as a header 855 authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 856 if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { 857 // to increase compatibility to existing api it is defaulting to be empty 858 authConfig = &cliconfig.AuthConfig{} 859 } 860 } else { 861 // the old format is supported for compatibility if there was no authConfig header 862 if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { 863 return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err) 864 } 865 } 866 867 name := vars["name"] 868 output := ioutils.NewWriteFlusher(w) 869 imagePushConfig := &graph.ImagePushConfig{ 870 MetaHeaders: metaHeaders, 871 AuthConfig: authConfig, 872 Tag: r.Form.Get("tag"), 873 OutStream: output, 874 } 875 876 w.Header().Set("Content-Type", "application/json") 877 878 if err := s.daemon.Repositories().Push(name, imagePushConfig); err != nil { 879 if !output.Flushed() { 880 return err 881 } 882 sf := streamformatter.NewJSONStreamFormatter() 883 output.Write(sf.FormatError(err)) 884 } 885 return nil 886 887 } 888 889 func (s *Server) getImagesGet(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 890 if vars == nil { 891 return fmt.Errorf("Missing parameter") 892 } 893 if err := parseForm(r); err != nil { 894 return err 895 } 896 897 w.Header().Set("Content-Type", "application/x-tar") 898 899 output := ioutils.NewWriteFlusher(w) 900 imageExportConfig := &graph.ImageExportConfig{Outstream: output} 901 if name, ok := vars["name"]; ok { 902 imageExportConfig.Names = []string{name} 903 } else { 904 imageExportConfig.Names = r.Form["names"] 905 } 906 907 if err := s.daemon.Repositories().ImageExport(imageExportConfig); err != nil { 908 if !output.Flushed() { 909 return err 910 } 911 sf := streamformatter.NewJSONStreamFormatter() 912 output.Write(sf.FormatError(err)) 913 } 914 return nil 915 916 } 917 918 func (s *Server) postImagesLoad(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 919 return s.daemon.Repositories().Load(r.Body, w) 920 } 921 922 func (s *Server) postContainersCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 923 if err := parseForm(r); err != nil { 924 return err 925 } 926 if err := checkForJson(r); err != nil { 927 return err 928 } 929 var ( 930 warnings []string 931 name = r.Form.Get("name") 932 ) 933 934 config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body) 935 if err != nil { 936 return err 937 } 938 adjustCpuShares(version, hostConfig) 939 940 containerId, warnings, err := s.daemon.ContainerCreate(name, config, hostConfig) 941 if err != nil { 942 return err 943 } 944 945 return writeJSON(w, http.StatusCreated, &types.ContainerCreateResponse{ 946 ID: containerId, 947 Warnings: warnings, 948 }) 949 } 950 951 func (s *Server) postContainersRestart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 952 if err := parseForm(r); err != nil { 953 return err 954 } 955 if vars == nil { 956 return fmt.Errorf("Missing parameter") 957 } 958 959 timeout, _ := strconv.Atoi(r.Form.Get("t")) 960 961 if err := s.daemon.ContainerRestart(vars["name"], timeout); err != nil { 962 return err 963 } 964 965 w.WriteHeader(http.StatusNoContent) 966 967 return nil 968 } 969 970 func (s *Server) postContainerRename(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 971 if err := parseForm(r); err != nil { 972 return err 973 } 974 if vars == nil { 975 return fmt.Errorf("Missing parameter") 976 } 977 978 name := vars["name"] 979 newName := r.Form.Get("name") 980 if err := s.daemon.ContainerRename(name, newName); err != nil { 981 return err 982 } 983 w.WriteHeader(http.StatusNoContent) 984 return nil 985 } 986 987 func (s *Server) deleteContainers(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 988 if err := parseForm(r); err != nil { 989 return err 990 } 991 if vars == nil { 992 return fmt.Errorf("Missing parameter") 993 } 994 995 name := vars["name"] 996 config := &daemon.ContainerRmConfig{ 997 ForceRemove: boolValue(r, "force"), 998 RemoveVolume: boolValue(r, "v"), 999 RemoveLink: boolValue(r, "link"), 1000 } 1001 1002 if err := s.daemon.ContainerRm(name, config); err != nil { 1003 // Force a 404 for the empty string 1004 if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") { 1005 return fmt.Errorf("no such id: \"\"") 1006 } 1007 return err 1008 } 1009 1010 w.WriteHeader(http.StatusNoContent) 1011 1012 return nil 1013 } 1014 1015 func (s *Server) deleteImages(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1016 if err := parseForm(r); err != nil { 1017 return err 1018 } 1019 if vars == nil { 1020 return fmt.Errorf("Missing parameter") 1021 } 1022 1023 name := vars["name"] 1024 force := boolValue(r, "force") 1025 noprune := boolValue(r, "noprune") 1026 1027 list, err := s.daemon.ImageDelete(name, force, noprune) 1028 if err != nil { 1029 return err 1030 } 1031 1032 return writeJSON(w, http.StatusOK, list) 1033 } 1034 1035 func (s *Server) postContainersStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1036 if vars == nil { 1037 return fmt.Errorf("Missing parameter") 1038 } 1039 1040 // If contentLength is -1, we can assumed chunked encoding 1041 // or more technically that the length is unknown 1042 // https://golang.org/src/pkg/net/http/request.go#L139 1043 // net/http otherwise seems to swallow any headers related to chunked encoding 1044 // including r.TransferEncoding 1045 // allow a nil body for backwards compatibility 1046 var hostConfig *runconfig.HostConfig 1047 if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) { 1048 if err := checkForJson(r); err != nil { 1049 return err 1050 } 1051 1052 c, err := runconfig.DecodeHostConfig(r.Body) 1053 if err != nil { 1054 return err 1055 } 1056 1057 hostConfig = c 1058 } 1059 1060 if err := s.daemon.ContainerStart(vars["name"], hostConfig); err != nil { 1061 if err.Error() == "Container already started" { 1062 w.WriteHeader(http.StatusNotModified) 1063 return nil 1064 } 1065 return err 1066 } 1067 w.WriteHeader(http.StatusNoContent) 1068 return nil 1069 } 1070 1071 func (s *Server) postContainersStop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1072 if err := parseForm(r); err != nil { 1073 return err 1074 } 1075 if vars == nil { 1076 return fmt.Errorf("Missing parameter") 1077 } 1078 1079 seconds, _ := strconv.Atoi(r.Form.Get("t")) 1080 1081 if err := s.daemon.ContainerStop(vars["name"], seconds); err != nil { 1082 if err.Error() == "Container already stopped" { 1083 w.WriteHeader(http.StatusNotModified) 1084 return nil 1085 } 1086 return err 1087 } 1088 w.WriteHeader(http.StatusNoContent) 1089 1090 return nil 1091 } 1092 1093 func (s *Server) postContainersWait(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1094 if vars == nil { 1095 return fmt.Errorf("Missing parameter") 1096 } 1097 1098 status, err := s.daemon.ContainerWait(vars["name"], -1*time.Second) 1099 if err != nil { 1100 return err 1101 } 1102 1103 return writeJSON(w, http.StatusOK, &types.ContainerWaitResponse{ 1104 StatusCode: status, 1105 }) 1106 } 1107 1108 func (s *Server) postContainersResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1109 if err := parseForm(r); err != nil { 1110 return err 1111 } 1112 if vars == nil { 1113 return fmt.Errorf("Missing parameter") 1114 } 1115 1116 height, err := strconv.Atoi(r.Form.Get("h")) 1117 if err != nil { 1118 return err 1119 } 1120 width, err := strconv.Atoi(r.Form.Get("w")) 1121 if err != nil { 1122 return err 1123 } 1124 1125 return s.daemon.ContainerResize(vars["name"], height, width) 1126 } 1127 1128 func (s *Server) postContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1129 if err := parseForm(r); err != nil { 1130 return err 1131 } 1132 if vars == nil { 1133 return fmt.Errorf("Missing parameter") 1134 } 1135 1136 cont, err := s.daemon.Get(vars["name"]) 1137 if err != nil { 1138 return err 1139 } 1140 1141 inStream, outStream, err := hijackServer(w) 1142 if err != nil { 1143 return err 1144 } 1145 defer closeStreams(inStream, outStream) 1146 1147 if _, ok := r.Header["Upgrade"]; ok { 1148 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") 1149 } else { 1150 fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") 1151 } 1152 1153 attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{ 1154 InStream: inStream, 1155 OutStream: outStream, 1156 UseStdin: boolValue(r, "stdin"), 1157 UseStdout: boolValue(r, "stdout"), 1158 UseStderr: boolValue(r, "stderr"), 1159 Logs: boolValue(r, "logs"), 1160 Stream: boolValue(r, "stream"), 1161 } 1162 1163 if err := s.daemon.ContainerAttachWithLogs(cont, attachWithLogsConfig); err != nil { 1164 fmt.Fprintf(outStream, "Error attaching: %s\n", err) 1165 } 1166 1167 return nil 1168 } 1169 1170 func (s *Server) wsContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1171 if err := parseForm(r); err != nil { 1172 return err 1173 } 1174 if vars == nil { 1175 return fmt.Errorf("Missing parameter") 1176 } 1177 1178 cont, err := s.daemon.Get(vars["name"]) 1179 if err != nil { 1180 return err 1181 } 1182 1183 h := websocket.Handler(func(ws *websocket.Conn) { 1184 defer ws.Close() 1185 1186 wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{ 1187 InStream: ws, 1188 OutStream: ws, 1189 ErrStream: ws, 1190 Logs: boolValue(r, "logs"), 1191 Stream: boolValue(r, "stream"), 1192 } 1193 1194 if err := s.daemon.ContainerWsAttachWithLogs(cont, wsAttachWithLogsConfig); err != nil { 1195 logrus.Errorf("Error attaching websocket: %s", err) 1196 } 1197 }) 1198 h.ServeHTTP(w, r) 1199 1200 return nil 1201 } 1202 1203 func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1204 if vars == nil { 1205 return fmt.Errorf("Missing parameter") 1206 } 1207 1208 if version.LessThan("1.20") { 1209 containerJSONRaw, err := s.daemon.ContainerInspectPre120(vars["name"]) 1210 if err != nil { 1211 return err 1212 } 1213 return writeJSON(w, http.StatusOK, containerJSONRaw) 1214 } 1215 1216 containerJSON, err := s.daemon.ContainerInspect(vars["name"]) 1217 if err != nil { 1218 return err 1219 } 1220 return writeJSON(w, http.StatusOK, containerJSON) 1221 } 1222 1223 func (s *Server) getExecByID(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1224 if vars == nil { 1225 return fmt.Errorf("Missing parameter 'id'") 1226 } 1227 1228 eConfig, err := s.daemon.ContainerExecInspect(vars["id"]) 1229 if err != nil { 1230 return err 1231 } 1232 1233 return writeJSON(w, http.StatusOK, eConfig) 1234 } 1235 1236 func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1237 if vars == nil { 1238 return fmt.Errorf("Missing parameter") 1239 } 1240 1241 imageInspect, err := s.daemon.Repositories().Lookup(vars["name"]) 1242 if err != nil { 1243 return err 1244 } 1245 1246 return writeJSON(w, http.StatusOK, imageInspect) 1247 } 1248 1249 func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1250 var ( 1251 authConfigs = map[string]cliconfig.AuthConfig{} 1252 authConfigsEncoded = r.Header.Get("X-Registry-Config") 1253 buildConfig = builder.NewBuildConfig() 1254 ) 1255 1256 if authConfigsEncoded != "" { 1257 authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded)) 1258 if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); err != nil { 1259 // for a pull it is not an error if no auth was given 1260 // to increase compatibility with the existing api it is defaulting 1261 // to be empty. 1262 } 1263 } 1264 1265 w.Header().Set("Content-Type", "application/json") 1266 1267 if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") { 1268 buildConfig.Remove = true 1269 } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { 1270 buildConfig.Remove = true 1271 } else { 1272 buildConfig.Remove = boolValue(r, "rm") 1273 } 1274 if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") { 1275 buildConfig.Pull = true 1276 } 1277 1278 output := ioutils.NewWriteFlusher(w) 1279 buildConfig.Stdout = output 1280 buildConfig.Context = r.Body 1281 1282 buildConfig.RemoteURL = r.FormValue("remote") 1283 buildConfig.DockerfileName = r.FormValue("dockerfile") 1284 buildConfig.RepoName = r.FormValue("t") 1285 buildConfig.SuppressOutput = boolValue(r, "q") 1286 buildConfig.NoCache = boolValue(r, "nocache") 1287 buildConfig.ForceRemove = boolValue(r, "forcerm") 1288 buildConfig.AuthConfigs = authConfigs 1289 buildConfig.MemorySwap = int64ValueOrZero(r, "memswap") 1290 buildConfig.Memory = int64ValueOrZero(r, "memory") 1291 buildConfig.CPUShares = int64ValueOrZero(r, "cpushares") 1292 buildConfig.CPUPeriod = int64ValueOrZero(r, "cpuperiod") 1293 buildConfig.CPUQuota = int64ValueOrZero(r, "cpuquota") 1294 buildConfig.CPUSetCpus = r.FormValue("cpusetcpus") 1295 buildConfig.CPUSetMems = r.FormValue("cpusetmems") 1296 buildConfig.CgroupParent = r.FormValue("cgroupparent") 1297 1298 var buildUlimits = []*ulimit.Ulimit{} 1299 ulimitsJson := r.FormValue("ulimits") 1300 if ulimitsJson != "" { 1301 if err := json.NewDecoder(strings.NewReader(ulimitsJson)).Decode(&buildUlimits); err != nil { 1302 return err 1303 } 1304 buildConfig.Ulimits = buildUlimits 1305 } 1306 1307 // Job cancellation. Note: not all job types support this. 1308 if closeNotifier, ok := w.(http.CloseNotifier); ok { 1309 finished := make(chan struct{}) 1310 defer close(finished) 1311 go func() { 1312 select { 1313 case <-finished: 1314 case <-closeNotifier.CloseNotify(): 1315 logrus.Infof("Client disconnected, cancelling job: build") 1316 buildConfig.Cancel() 1317 } 1318 }() 1319 } 1320 1321 if err := builder.Build(s.daemon, buildConfig); err != nil { 1322 // Do not write the error in the http output if it's still empty. 1323 // This prevents from writing a 200(OK) when there is an interal error. 1324 if !output.Flushed() { 1325 return err 1326 } 1327 sf := streamformatter.NewJSONStreamFormatter() 1328 w.Write(sf.FormatError(err)) 1329 } 1330 return nil 1331 } 1332 1333 // postContainersCopy is deprecated in favor of getContainersArchivePath. 1334 func (s *Server) postContainersCopy(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1335 if vars == nil { 1336 return fmt.Errorf("Missing parameter") 1337 } 1338 1339 if err := checkForJson(r); err != nil { 1340 return err 1341 } 1342 1343 cfg := types.CopyConfig{} 1344 if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { 1345 return err 1346 } 1347 1348 if cfg.Resource == "" { 1349 return fmt.Errorf("Path cannot be empty") 1350 } 1351 1352 data, err := s.daemon.ContainerCopy(vars["name"], cfg.Resource) 1353 if err != nil { 1354 if strings.Contains(strings.ToLower(err.Error()), "no such id") { 1355 w.WriteHeader(http.StatusNotFound) 1356 return nil 1357 } 1358 if os.IsNotExist(err) { 1359 return fmt.Errorf("Could not find the file %s in container %s", cfg.Resource, vars["name"]) 1360 } 1361 return err 1362 } 1363 defer data.Close() 1364 1365 w.Header().Set("Content-Type", "application/x-tar") 1366 if _, err := io.Copy(w, data); err != nil { 1367 return err 1368 } 1369 1370 return nil 1371 } 1372 1373 // // Encode the stat to JSON, base64 encode, and place in a header. 1374 func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error { 1375 statJSON, err := json.Marshal(stat) 1376 if err != nil { 1377 return err 1378 } 1379 1380 header.Set( 1381 "X-Docker-Container-Path-Stat", 1382 base64.StdEncoding.EncodeToString(statJSON), 1383 ) 1384 1385 return nil 1386 } 1387 1388 func (s *Server) headContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1389 v, err := archiveFormValues(r, vars) 1390 if err != nil { 1391 return err 1392 } 1393 1394 stat, err := s.daemon.ContainerStatPath(v.name, v.path) 1395 if err != nil { 1396 return err 1397 } 1398 1399 return setContainerPathStatHeader(stat, w.Header()) 1400 } 1401 1402 func (s *Server) getContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1403 v, err := archiveFormValues(r, vars) 1404 if err != nil { 1405 return err 1406 } 1407 1408 tarArchive, stat, err := s.daemon.ContainerArchivePath(v.name, v.path) 1409 if err != nil { 1410 return err 1411 } 1412 defer tarArchive.Close() 1413 1414 if err := setContainerPathStatHeader(stat, w.Header()); err != nil { 1415 return err 1416 } 1417 1418 w.Header().Set("Content-Type", "application/x-tar") 1419 _, err = io.Copy(w, tarArchive) 1420 1421 return err 1422 } 1423 1424 func (s *Server) putContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1425 v, err := archiveFormValues(r, vars) 1426 if err != nil { 1427 return err 1428 } 1429 1430 noOverwriteDirNonDir := boolValue(r, "noOverwriteDirNonDir") 1431 return s.daemon.ContainerExtractToDir(v.name, v.path, noOverwriteDirNonDir, r.Body) 1432 } 1433 1434 func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1435 if err := parseForm(r); err != nil { 1436 return err 1437 } 1438 if err := checkForJson(r); err != nil { 1439 return err 1440 } 1441 name := vars["name"] 1442 1443 execConfig := &runconfig.ExecConfig{} 1444 if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil { 1445 return err 1446 } 1447 execConfig.Container = name 1448 1449 if len(execConfig.Cmd) == 0 { 1450 return fmt.Errorf("No exec command specified") 1451 } 1452 1453 // Register an instance of Exec in container. 1454 id, err := s.daemon.ContainerExecCreate(execConfig) 1455 if err != nil { 1456 logrus.Errorf("Error setting up exec command in container %s: %s", name, err) 1457 return err 1458 } 1459 1460 return writeJSON(w, http.StatusCreated, &types.ContainerExecCreateResponse{ 1461 ID: id, 1462 }) 1463 } 1464 1465 // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. 1466 func (s *Server) postContainerExecStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1467 if err := parseForm(r); err != nil { 1468 return err 1469 } 1470 var ( 1471 execName = vars["name"] 1472 stdin io.ReadCloser 1473 stdout io.Writer 1474 stderr io.Writer 1475 ) 1476 1477 execStartCheck := &types.ExecStartCheck{} 1478 if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil { 1479 return err 1480 } 1481 1482 if !execStartCheck.Detach { 1483 // Setting up the streaming http interface. 1484 inStream, outStream, err := hijackServer(w) 1485 if err != nil { 1486 return err 1487 } 1488 defer closeStreams(inStream, outStream) 1489 1490 var errStream io.Writer 1491 1492 if _, ok := r.Header["Upgrade"]; ok { 1493 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") 1494 } else { 1495 fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") 1496 } 1497 1498 if !execStartCheck.Tty { 1499 errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) 1500 outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) 1501 } 1502 1503 stdin = inStream 1504 stdout = outStream 1505 stderr = errStream 1506 } 1507 // Now run the user process in container. 1508 1509 if err := s.daemon.ContainerExecStart(execName, stdin, stdout, stderr); err != nil { 1510 logrus.Errorf("Error starting exec command in container %s: %s", execName, err) 1511 return err 1512 } 1513 w.WriteHeader(http.StatusNoContent) 1514 1515 return nil 1516 } 1517 1518 func (s *Server) postContainerExecResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1519 if err := parseForm(r); err != nil { 1520 return err 1521 } 1522 if vars == nil { 1523 return fmt.Errorf("Missing parameter") 1524 } 1525 1526 height, err := strconv.Atoi(r.Form.Get("h")) 1527 if err != nil { 1528 return err 1529 } 1530 width, err := strconv.Atoi(r.Form.Get("w")) 1531 if err != nil { 1532 return err 1533 } 1534 1535 return s.daemon.ContainerExecResize(vars["name"], height, width) 1536 } 1537 1538 func (s *Server) optionsHandler(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1539 w.WriteHeader(http.StatusOK) 1540 return nil 1541 } 1542 func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string) { 1543 logrus.Debugf("CORS header is enabled and set to: %s", corsHeaders) 1544 w.Header().Add("Access-Control-Allow-Origin", corsHeaders) 1545 w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth") 1546 w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") 1547 } 1548 1549 func (s *Server) ping(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 1550 _, err := w.Write([]byte{'O', 'K'}) 1551 return err 1552 } 1553 1554 func (s *Server) initTcpSocket(addr string) (l net.Listener, err error) { 1555 if s.cfg.TLSConfig == nil || s.cfg.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert { 1556 logrus.Warn("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") 1557 } 1558 if l, err = sockets.NewTcpSocket(addr, s.cfg.TLSConfig, s.start); err != nil { 1559 return nil, err 1560 } 1561 if err := allocateDaemonPort(addr); err != nil { 1562 return nil, err 1563 } 1564 return 1565 } 1566 1567 func makeHttpHandler(logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, corsHeaders string, dockerVersion version.Version) http.HandlerFunc { 1568 return func(w http.ResponseWriter, r *http.Request) { 1569 // log the request 1570 logrus.Debugf("Calling %s %s", localMethod, localRoute) 1571 1572 if logging { 1573 logrus.Infof("%s %s", r.Method, r.RequestURI) 1574 } 1575 1576 if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") { 1577 userAgent := strings.Split(r.Header.Get("User-Agent"), "/") 1578 1579 // v1.20 onwards includes the GOOS of the client after the version 1580 // such as Docker/1.7.0 (linux) 1581 if len(userAgent) == 2 && strings.Contains(userAgent[1], " ") { 1582 userAgent[1] = strings.Split(userAgent[1], " ")[0] 1583 } 1584 1585 if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) { 1586 logrus.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion) 1587 } 1588 } 1589 version := version.Version(mux.Vars(r)["version"]) 1590 if version == "" { 1591 version = api.Version 1592 } 1593 if corsHeaders != "" { 1594 writeCorsHeaders(w, r, corsHeaders) 1595 } 1596 1597 if version.GreaterThan(api.Version) { 1598 http.Error(w, fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", version, api.Version).Error(), http.StatusBadRequest) 1599 return 1600 } 1601 if version.LessThan(api.MinVersion) { 1602 http.Error(w, fmt.Errorf("client is too old, minimum supported API version is %s, please upgrade your client to a newer version", api.MinVersion).Error(), http.StatusBadRequest) 1603 return 1604 } 1605 1606 w.Header().Set("Server", "Docker/"+dockerversion.VERSION+" ("+runtime.GOOS+")") 1607 1608 if err := handlerFunc(version, w, r, mux.Vars(r)); err != nil { 1609 logrus.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err) 1610 httpError(w, err) 1611 } 1612 } 1613 } 1614 1615 // we keep enableCors just for legacy usage, need to be removed in the future 1616 func createRouter(s *Server) *mux.Router { 1617 r := mux.NewRouter() 1618 if os.Getenv("DEBUG") != "" { 1619 ProfilerSetup(r, "/debug/") 1620 } 1621 m := map[string]map[string]HttpApiFunc{ 1622 "HEAD": { 1623 "/containers/{name:.*}/archive": s.headContainersArchive, 1624 }, 1625 "GET": { 1626 "/_ping": s.ping, 1627 "/events": s.getEvents, 1628 "/info": s.getInfo, 1629 "/version": s.getVersion, 1630 "/images/json": s.getImagesJSON, 1631 "/images/search": s.getImagesSearch, 1632 "/images/get": s.getImagesGet, 1633 "/images/{name:.*}/get": s.getImagesGet, 1634 "/images/{name:.*}/history": s.getImagesHistory, 1635 "/images/{name:.*}/json": s.getImagesByName, 1636 "/containers/ps": s.getContainersJSON, 1637 "/containers/json": s.getContainersJSON, 1638 "/containers/{name:.*}/export": s.getContainersExport, 1639 "/containers/{name:.*}/changes": s.getContainersChanges, 1640 "/containers/{name:.*}/json": s.getContainersByName, 1641 "/containers/{name:.*}/top": s.getContainersTop, 1642 "/containers/{name:.*}/logs": s.getContainersLogs, 1643 "/containers/{name:.*}/stats": s.getContainersStats, 1644 "/containers/{name:.*}/attach/ws": s.wsContainersAttach, 1645 "/exec/{id:.*}/json": s.getExecByID, 1646 "/containers/{name:.*}/archive": s.getContainersArchive, 1647 }, 1648 "POST": { 1649 "/auth": s.postAuth, 1650 "/commit": s.postCommit, 1651 "/build": s.postBuild, 1652 "/images/create": s.postImagesCreate, 1653 "/images/load": s.postImagesLoad, 1654 "/images/{name:.*}/push": s.postImagesPush, 1655 "/images/{name:.*}/tag": s.postImagesTag, 1656 "/containers/create": s.postContainersCreate, 1657 "/containers/{name:.*}/kill": s.postContainersKill, 1658 "/containers/{name:.*}/pause": s.postContainersPause, 1659 "/containers/{name:.*}/unpause": s.postContainersUnpause, 1660 "/containers/{name:.*}/restart": s.postContainersRestart, 1661 "/containers/{name:.*}/start": s.postContainersStart, 1662 "/containers/{name:.*}/stop": s.postContainersStop, 1663 "/containers/{name:.*}/wait": s.postContainersWait, 1664 "/containers/{name:.*}/resize": s.postContainersResize, 1665 "/containers/{name:.*}/attach": s.postContainersAttach, 1666 "/containers/{name:.*}/copy": s.postContainersCopy, 1667 "/containers/{name:.*}/exec": s.postContainerExecCreate, 1668 "/exec/{name:.*}/start": s.postContainerExecStart, 1669 "/exec/{name:.*}/resize": s.postContainerExecResize, 1670 "/containers/{name:.*}/rename": s.postContainerRename, 1671 }, 1672 "PUT": { 1673 "/containers/{name:.*}/archive": s.putContainersArchive, 1674 }, 1675 "DELETE": { 1676 "/containers/{name:.*}": s.deleteContainers, 1677 "/images/{name:.*}": s.deleteImages, 1678 }, 1679 "OPTIONS": { 1680 "": s.optionsHandler, 1681 }, 1682 } 1683 1684 // If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*" 1685 // otherwise, all head values will be passed to HTTP handler 1686 corsHeaders := s.cfg.CorsHeaders 1687 if corsHeaders == "" && s.cfg.EnableCors { 1688 corsHeaders = "*" 1689 } 1690 1691 for method, routes := range m { 1692 for route, fct := range routes { 1693 logrus.Debugf("Registering %s, %s", method, route) 1694 // NOTE: scope issue, make sure the variables are local and won't be changed 1695 localRoute := route 1696 localFct := fct 1697 localMethod := method 1698 1699 // build the handler function 1700 f := makeHttpHandler(s.cfg.Logging, localMethod, localRoute, localFct, corsHeaders, version.Version(s.cfg.Version)) 1701 1702 // add the new route 1703 if localRoute == "" { 1704 r.Methods(localMethod).HandlerFunc(f) 1705 } else { 1706 r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f) 1707 r.Path(localRoute).Methods(localMethod).HandlerFunc(f) 1708 } 1709 } 1710 } 1711 1712 return r 1713 }