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