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