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