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