From 6d5bdff3942ce5e030b2cbd1510f418de25a1a53 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 3 Jun 2013 21:39:00 -0400 Subject: [PATCH 01/13] Add flag to enable cross domain requests in Api Add the -api-enable-cors flag when running docker in daemon mode to allow CORS requests to be made to the Remote Api. The default value is false for this flag to not allow cross origin request to be made. Also added a handler for OPTIONS requests the standard for cross domain requests is to initially make an OPTIONS request to the api. --- api.go | 13 +++++++++++++ api_test.go | 26 ++++++++++++++++++++++++++ docker/docker.go | 7 ++++--- docs/sources/api/docker_remote_api.rst | 8 ++++++++ server.go | 8 +++++--- 5 files changed, 56 insertions(+), 6 deletions(-) diff --git a/api.go b/api.go index 7666b79a5..978cd296a 100644 --- a/api.go +++ b/api.go @@ -703,6 +703,11 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ return nil } +func writeCorsHeaders(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Access-Control-Allow-Origin", "*") + w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") +} + func ListenAndServe(addr string, srv *Server, logging bool) error { r := mux.NewRouter() log.Printf("Listening for HTTP on %s\n", addr) @@ -773,12 +778,20 @@ func ListenAndServe(addr string, srv *Server, logging bool) error { w.WriteHeader(http.StatusNotFound) return } + if srv.enableCors { + writeCorsHeaders(w, r) + } if err := localFct(srv, version, w, r, mux.Vars(r)); err != nil { httpError(w, err) } } r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f) r.Path(localRoute).Methods(localMethod).HandlerFunc(f) + r.Methods("OPTIONS").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if srv.enableCors { + writeCorsHeaders(w, r) + } + }) } } return http.ListenAndServe(addr, r) diff --git a/api_test.go b/api_test.go index 9121167e1..e464e5110 100644 --- a/api_test.go +++ b/api_test.go @@ -1239,6 +1239,32 @@ func TestDeleteContainers(t *testing.T) { } } +func TestGetEnabledCors(t *testing.T) { + runtime, err := newTestRuntime() + if err != nil { + t.Fatal(err) + } + defer nuke(runtime) + + srv := &Server{runtime: runtime, enableCors: true} + + r := httptest.NewRecorder() + + if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil { + t.Fatal(err) + } + + allowOrigin := r.Header().Get("Access-Control-Allow-Origin") + allowHeaders := r.Header().Get("Access-Control-Allow-Headers") + + if allowOrigin != "*" { + t.Errorf("Expected header Access-Control-Allow-Origin to be \"*\", %s found.", allowOrigin) + } + if allowHeaders != "Origin, X-Requested-With, Content-Type, Accept" { + t.Errorf("Expected header Access-Control-Allow-Headers to be \"Origin, X-Requested-With, Content-Type, Accept\", %s found.", allowHeaders) + } +} + func TestDeleteImages(t *testing.T) { //FIXME: Implement this test t.Log("Test not implemented") diff --git a/docker/docker.go b/docker/docker.go index 7b8aa7f85..dd804f81c 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -33,6 +33,7 @@ func main() { bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge") pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID") flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to") + flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.") flag.Parse() if *bridgeName != "" { docker.NetworkBridgeIface = *bridgeName @@ -65,7 +66,7 @@ func main() { flag.Usage() return } - if err := daemon(*pidfile, host, port, *flAutoRestart); err != nil { + if err := daemon(*pidfile, host, port, *flAutoRestart, *flEnableCors); err != nil { log.Fatal(err) os.Exit(-1) } @@ -104,7 +105,7 @@ func removePidFile(pidfile string) { } } -func daemon(pidfile, addr string, port int, autoRestart bool) error { +func daemon(pidfile, addr string, port int, autoRestart, enableCors bool) error { if addr != "127.0.0.1" { log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } @@ -122,7 +123,7 @@ func daemon(pidfile, addr string, port int, autoRestart bool) error { os.Exit(0) }() - server, err := docker.NewServer(autoRestart) + server, err := docker.NewServer(autoRestart, enableCors) if err != nil { return err } diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index dca4599c5..e59b93d62 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -1056,3 +1056,11 @@ Here are the steps of 'docker run' : In this first version of the API, some of the endpoints, like /attach, /pull or /push uses hijacking to transport stdin, stdout and stderr on the same socket. This might change in the future. + + +3.3 CORS Requests +----------------- + +To enable cross origin requests to the remote api add the flag "-api-enable-cors" when running docker in daemon mode. + + docker -d -H="192.168.1.9:4243" -api-enable-cors diff --git a/server.go b/server.go index 08cb37a72..d86ffe0f4 100644 --- a/server.go +++ b/server.go @@ -870,7 +870,7 @@ func (srv *Server) ImageInspect(name string) (*Image, error) { return nil, fmt.Errorf("No such image: %s", name) } -func NewServer(autoRestart bool) (*Server, error) { +func NewServer(autoRestart, enableCors bool) (*Server, error) { if runtime.GOARCH != "amd64" { log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH) } @@ -879,12 +879,14 @@ func NewServer(autoRestart bool) (*Server, error) { return nil, err } srv := &Server{ - runtime: runtime, + runtime: runtime, + enableCors: enableCors, } runtime.srv = srv return srv, nil } type Server struct { - runtime *Runtime + runtime *Runtime + enableCors bool } From 0f23fb949dd894ff70641cc01ac116aa55083157 Mon Sep 17 00:00:00 2001 From: Thatcher Peskens Date: Wed, 5 Jun 2013 18:06:51 -0700 Subject: [PATCH 02/13] Fixed some links * Added Google group to FAQ on docs * Changed IRC link * Fixed link to contributing broken by 326faec --- docs/sources/contributing/contributing.rst | 2 +- docs/sources/faq.rst | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/sources/contributing/contributing.rst b/docs/sources/contributing/contributing.rst index 25b4df763..1913cec30 100644 --- a/docs/sources/contributing/contributing.rst +++ b/docs/sources/contributing/contributing.rst @@ -5,5 +5,5 @@ Contributing to Docker ====================== -Want to hack on Docker? Awesome! The repository includes `all the instructions you need to get started `. +Want to hack on Docker? Awesome! The repository includes `all the instructions you need to get started `_. diff --git a/docs/sources/faq.rst b/docs/sources/faq.rst index dfffa012f..12b975133 100644 --- a/docs/sources/faq.rst +++ b/docs/sources/faq.rst @@ -35,13 +35,16 @@ Most frequently asked questions. You can find more answers on: - * `IRC: docker on freenode`_ + * `Docker club mailinglist`_ + * `IRC, docker on freenode`_ * `Github`_ * `Ask questions on Stackoverflow`_ * `Join the conversation on Twitter`_ + + .. _Docker club mailinglist: https://groups.google.com/d/forum/docker-club .. _the repo: http://www.github.com/dotcloud/docker - .. _IRC: docker on freenode: docker on freenode: irc://chat.freenode.net#docker + .. _IRC, docker on freenode: irc://chat.freenode.net#docker .. _Github: http://www.github.com/dotcloud/docker .. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker .. _Join the conversation on Twitter: http://twitter.com/getdocker From 5e6cd21f8b3bded9e9cba2e1b9a754df5a9c2bf2 Mon Sep 17 00:00:00 2001 From: Sam J Sharpe Date: Fri, 7 Jun 2013 20:32:27 +0100 Subject: [PATCH 03/13] Build from Dockerfile on stdin requires a hypen There is a missing hypen in the documentation: `docker build < Dockerfile` will complain `docker build - < Dockerfile` will not complain --- docs/sources/use/builder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/use/builder.rst b/docs/sources/use/builder.rst index abd5b9ecb..f2e9ce97c 100644 --- a/docs/sources/use/builder.rst +++ b/docs/sources/use/builder.rst @@ -18,7 +18,7 @@ steps and commit them along the way, giving you a final image. To use Docker Builder, assemble the steps into a text file (commonly referred to as a Dockerfile) and supply this to `docker build` on STDIN, like so: - ``docker build < Dockerfile`` + ``docker build - < Dockerfile`` Docker will run your steps one-by-one, committing the result if necessary, before finally outputting the ID of your new image. From 393e873d25093f579d1a293bc473007b04f3c239 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Sun, 9 Jun 2013 17:17:35 -0900 Subject: [PATCH 04/13] Add Access-Control-Allow-Methods header Add the Access-Control-Allow-Methods header so that DELETE operations are allowed. Also move the write CORS headers method before docker writes a 404 not found so that the client receives the correct response and not an invalid CORS request. --- api.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/api.go b/api.go index 978cd296a..3831f326f 100644 --- a/api.go +++ b/api.go @@ -706,6 +706,7 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ func writeCorsHeaders(w http.ResponseWriter, r *http.Request) { w.Header().Add("Access-Control-Allow-Origin", "*") w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") + w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") } func ListenAndServe(addr string, srv *Server, logging bool) error { @@ -774,13 +775,13 @@ func ListenAndServe(addr string, srv *Server, logging bool) error { if err != nil { version = API_VERSION } + if srv.enableCors { + writeCorsHeaders(w, r) + } if version == 0 || version > API_VERSION { w.WriteHeader(http.StatusNotFound) return } - if srv.enableCors { - writeCorsHeaders(w, r) - } if err := localFct(srv, version, w, r, mux.Vars(r)); err != nil { httpError(w, err) } From 37c20fa64b7fc8156d51e08f9271fe3dafda5c19 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Mon, 10 Jun 2013 19:03:54 +0300 Subject: [PATCH 05/13] Fixed broken link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b65f5eeb2..bcad502ab 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ Note ---- We also keep the documentation in this repository. The website documentation is generated using sphinx using these sources. -Please find it under docs/sources/ and read more about it https://github.com/dotcloud/docker/master/docs/README.md +Please find it under docs/sources/ and read more about it https://github.com/dotcloud/docker/tree/master/docs/README.md Please feel free to fix / update the documentation and send us pull requests. More tutorials are also welcome. From 4e180107314a0a7768120ed66e615c8f668677bd Mon Sep 17 00:00:00 2001 From: shin- Date: Tue, 4 Jun 2013 09:29:47 -0700 Subject: [PATCH 06/13] Support for special namespace 'src' (highland support) --- server.go | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/server.go b/server.go index 666612365..9815b510f 100644 --- a/server.go +++ b/server.go @@ -330,8 +330,8 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin return nil } -func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, askedTag string, sf *utils.StreamFormatter) error { - out.Write(sf.FormatStatus("Pulling repository %s from %s", remote, auth.IndexServerAddress())) +func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, local, remote, askedTag string, sf *utils.StreamFormatter) error { + out.Write(sf.FormatStatus("Pulling repository %s from %s", local, auth.IndexServerAddress())) repoData, err := r.GetRepositoryData(remote) if err != nil { return err @@ -358,7 +358,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a // Otherwise, check that the tag exists and use only that one id, exists := tagsList[askedTag] if !exists { - return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, remote) + return fmt.Errorf("Tag %s not found in repositoy %s", askedTag, local) } repoData.ImgList[id].Tag = askedTag } @@ -386,7 +386,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, remote, a if askedTag != "" && tag != askedTag { continue } - if err := srv.runtime.repositories.Set(remote, tag, id, true); err != nil { + if err := srv.runtime.repositories.Set(local, tag, id, true); err != nil { return err } } @@ -406,8 +406,12 @@ func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *util } return nil } - - if err := srv.pullRepository(r, out, name, tag, sf); err != nil { + remote := name + parts := strings.Split(name, "/") + if len(parts) > 2 { + remote = fmt.Sprintf("src/%s", strings.Join(parts, "%2F")) + } + if err := srv.pullRepository(r, out, name, remote, tag, sf); err != nil { return err } @@ -489,7 +493,13 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri } out.Write(sf.FormatStatus("Sending image list")) - repoData, err := r.PushImageJSONIndex(name, imgList, false) + srvName := name + parts := strings.Split(name, "/") + if len(parts) > 2 { + srvName = fmt.Sprintf("src/%s", strings.Join(parts, "%2F")) + } + + repoData, err := r.PushImageJSONIndex(srvName, imgList, false) if err != nil { return err } @@ -506,14 +516,14 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri // FIXME: Continue on error? return err } - out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+name+"/"+elem.Tag)) - if err := r.PushRegistryTag(name, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil { + out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+srvName+"/"+elem.Tag)) + if err := r.PushRegistryTag(srvName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil { return err } } } - if _, err := r.PushImageJSONIndex(name, imgList, true); err != nil { + if _, err := r.PushImageJSONIndex(srvName, imgList, true); err != nil { return err } return nil From d227af1edd5366df91f96193ddff3ff43d54b4f4 Mon Sep 17 00:00:00 2001 From: shin- Date: Wed, 5 Jun 2013 08:54:33 -0700 Subject: [PATCH 07/13] Escape remote names on repo push/pull --- server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server.go b/server.go index 9815b510f..f34235b05 100644 --- a/server.go +++ b/server.go @@ -409,7 +409,7 @@ func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *util remote := name parts := strings.Split(name, "/") if len(parts) > 2 { - remote = fmt.Sprintf("src/%s", strings.Join(parts, "%2F")) + remote = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/"))) } if err := srv.pullRepository(r, out, name, remote, tag, sf); err != nil { return err @@ -496,7 +496,7 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri srvName := name parts := strings.Split(name, "/") if len(parts) > 2 { - srvName = fmt.Sprintf("src/%s", strings.Join(parts, "%2F")) + srvName = fmt.Sprintf("src/%s", url.QueryEscape(strings.Join(parts, "/"))) } repoData, err := r.PushImageJSONIndex(srvName, imgList, false) From b1ed75078ea04de8725ce5c39ae3b463f86faf4e Mon Sep 17 00:00:00 2001 From: Francisco Souza Date: Mon, 10 Jun 2013 16:07:57 -0300 Subject: [PATCH 08/13] docs/api/remote: fix rst syntax in the "Search images" section --- docs/sources/api/docker_remote_api.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index 1c46cf148..2e9453ef6 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -839,9 +839,9 @@ Search images } ] - :query term: term to search - :statuscode 200: no error - :statuscode 500: server error + :query term: term to search + :statuscode 200: no error + :statuscode 500: server error 3.3 Misc From 0a28628c02d486512dc7e62eb54ccfd27eaa0a27 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 10 Jun 2013 13:02:40 -0900 Subject: [PATCH 09/13] Add Cors and OPTIONS route unit tests Move creating the router and populating the routes to a separate function outside of ListenAndServe to allow unit tests to make assertions on the configured routes and handler funcs. --- api.go | 23 ++++++++++++++++------- api_test.go | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/api.go b/api.go index 3831f326f..cc00849a6 100644 --- a/api.go +++ b/api.go @@ -709,9 +709,8 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request) { w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") } -func ListenAndServe(addr string, srv *Server, logging bool) error { +func createRouter(srv *Server, logging bool) (*mux.Router, error) { r := mux.NewRouter() - log.Printf("Listening for HTTP on %s\n", addr) m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{ "GET": { @@ -788,12 +787,22 @@ func ListenAndServe(addr string, srv *Server, logging bool) error { } r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f) r.Path(localRoute).Methods(localMethod).HandlerFunc(f) - r.Methods("OPTIONS").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if srv.enableCors { - writeCorsHeaders(w, r) - } - }) } } + r.Methods("OPTIONS").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if srv.enableCors { + writeCorsHeaders(w, r) + } + }) + return r, nil +} + +func ListenAndServe(addr string, srv *Server, logging bool) error { + log.Printf("Listening for HTTP on %s\n", addr) + + r, err := createRouter(srv, logging) + if err != nil { + return err + } return http.ListenAndServe(addr, r) } diff --git a/api_test.go b/api_test.go index e464e5110..748bcf812 100644 --- a/api_test.go +++ b/api_test.go @@ -1239,6 +1239,32 @@ func TestDeleteContainers(t *testing.T) { } } +func TestOptionsRoute(t *testing.T) { + runtime, err := newTestRuntime() + if err != nil { + t.Fatal(err) + } + defer nuke(runtime) + + srv := &Server{runtime: runtime, enableCors: true} + + r := httptest.NewRecorder() + router, err := createRouter(srv, false) + if err != nil { + t.Fatal(err) + } + + req, err := http.NewRequest("OPTIONS", "/", nil) + if err != nil { + t.Fatal(err) + } + + router.ServeHTTP(r, req) + if r.Code != 200 { + t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code) + } +} + func TestGetEnabledCors(t *testing.T) { runtime, err := newTestRuntime() if err != nil { @@ -1250,12 +1276,24 @@ func TestGetEnabledCors(t *testing.T) { r := httptest.NewRecorder() - if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil { + router, err := createRouter(srv, false) + if err != nil { t.Fatal(err) } + req, err := http.NewRequest("GET", "/version", nil) + if err != nil { + t.Fatal(err) + } + + router.ServeHTTP(r, req) + if r.Code != 200 { + t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code) + } + allowOrigin := r.Header().Get("Access-Control-Allow-Origin") allowHeaders := r.Header().Get("Access-Control-Allow-Headers") + allowMethods := r.Header().Get("Access-Control-Allow-Methods") if allowOrigin != "*" { t.Errorf("Expected header Access-Control-Allow-Origin to be \"*\", %s found.", allowOrigin) @@ -1263,6 +1301,9 @@ func TestGetEnabledCors(t *testing.T) { if allowHeaders != "Origin, X-Requested-With, Content-Type, Accept" { t.Errorf("Expected header Access-Control-Allow-Headers to be \"Origin, X-Requested-With, Content-Type, Accept\", %s found.", allowHeaders) } + if allowMethods != "GET, POST, DELETE, PUT, OPTIONS" { + t.Errorf("Expected hearder Access-Control-Allow-Methods to be \"GET, POST, DELETE, PUT, OPTIONS\", %s found.", allowMethods) + } } func TestDeleteImages(t *testing.T) { From eeea9ac9468a73bb9514a2d729060889b48384ee Mon Sep 17 00:00:00 2001 From: Andy Rothfusz Date: Mon, 10 Jun 2013 15:17:27 -0700 Subject: [PATCH 10/13] Add list of Docker Remote API Client Libraries. Fixes #800. --- docs/Makefile | 8 ++++---- docs/sources/api/docker_remote_api.rst | 24 ++++++++++++++++++++++++ docs/sources/api/index.rst | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index 517d01658..a97255f51 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -46,12 +46,11 @@ clean: -rm -rf $(BUILDDIR)/* docs: - #-rm -rf $(BUILDDIR)/* $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The documentation pages are now in $(BUILDDIR)/html." -server: +server: docs @cd $(BUILDDIR)/html; $(PYTHON) -m SimpleHTTPServer 8000 site: @@ -62,12 +61,13 @@ site: connect: @echo connecting dotcloud to www.docker.io website, make sure to use user 1 - @cd _build/website/ ; \ + @echo or create your own "dockerwebsite" app + @cd $(BUILDDIR)/website/ ; \ dotcloud connect dockerwebsite ; \ dotcloud list push: - @cd _build/website/ ; \ + @cd $(BUILDDIR)/website/ ; \ dotcloud push $(VERSIONS): diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index 1c46cf148..625544cc7 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -1056,3 +1056,27 @@ Here are the steps of 'docker run' : In this first version of the API, some of the endpoints, like /attach, /pull or /push uses hijacking to transport stdin, stdout and stderr on the same socket. This might change in the future. + +================================== +Docker Remote API Client Libraries +================================== + +These libraries have been not tested by the Docker Maintainers for +compatibility. Please file issues with the library owners. If you +find more library implementations, please list them in Docker doc bugs +and we will add the libraries here. + ++----------------------+----------------+--------------------------------------------+ +| Language/Framework | Name | Repository | ++======================+================+============================================+ +| Python | docker-py | https://github.com/dotcloud/docker-py | ++----------------------+----------------+--------------------------------------------+ +| Ruby | docker-ruby | https://github.com/ActiveState/docker-ruby | ++----------------------+----------------+--------------------------------------------+ +| Ruby | docker-client | https://github.com/geku/docker-client | ++----------------------+----------------+--------------------------------------------+ +| Javascript | docker-js | https://github.com/dgoujard/docker-js | ++----------------------+----------------+--------------------------------------------+ +| Javascript (Angular) | dockerui | https://github.com/crosbymichael/dockerui | +| **WebUI** | | | ++----------------------+----------------+--------------------------------------------+ diff --git a/docs/sources/api/index.rst b/docs/sources/api/index.rst index 85770f484..29de81d19 100644 --- a/docs/sources/api/index.rst +++ b/docs/sources/api/index.rst @@ -5,7 +5,7 @@ APIs ==== -This following : +Your programs and scripts can access Docker's functionality via these interfaces: .. toctree:: :maxdepth: 3 From ac599d652846f6456366b8028b2c38da0565d8b1 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 10 Jun 2013 14:44:10 -0900 Subject: [PATCH 11/13] Add explicit status response to OPTIONS handler Write the http.StatusOK header in the OPTIONS handler and update the unit tests to refer to the response code using the const from the http package. --- api.go | 1 + api_test.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/api.go b/api.go index cc00849a6..681b719c1 100644 --- a/api.go +++ b/api.go @@ -793,6 +793,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { if srv.enableCors { writeCorsHeaders(w, r) } + w.WriteHeader(http.StatusOK) }) return r, nil } diff --git a/api_test.go b/api_test.go index 748bcf812..62da94113 100644 --- a/api_test.go +++ b/api_test.go @@ -1260,7 +1260,7 @@ func TestOptionsRoute(t *testing.T) { } router.ServeHTTP(r, req) - if r.Code != 200 { + if r.Code != http.StatusOK { t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code) } } @@ -1287,7 +1287,7 @@ func TestGetEnabledCors(t *testing.T) { } router.ServeHTTP(r, req) - if r.Code != 200 { + if r.Code != http.StatusOK { t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code) } From dd53c457d75a49e6e140c6d71642b237f3ee9056 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 10 Jun 2013 16:10:40 -0900 Subject: [PATCH 12/13] Add OPTIONS to route map Move the OPTIONS method registration into the existing route map. Also add support for empty paths in the map. --- api.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/api.go b/api.go index 681b719c1..29ff76171 100644 --- a/api.go +++ b/api.go @@ -703,6 +703,10 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ return nil } +func optionsHandler(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + w.WriteHeader(http.StatusOK) + return nil +} func writeCorsHeaders(w http.ResponseWriter, r *http.Request) { w.Header().Add("Access-Control-Allow-Origin", "*") w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") @@ -750,6 +754,9 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { "/containers/{name:.*}": deleteContainers, "/images/{name:.*}": deleteImages, }, + "OPTIONS": { + "": optionsHandler, + }, } for method, routes := range m { @@ -785,16 +792,15 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { httpError(w, err) } } - r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f) - r.Path(localRoute).Methods(localMethod).HandlerFunc(f) + + if localRoute == "" { + r.Methods(localMethod).HandlerFunc(f) + } else { + r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f) + r.Path(localRoute).Methods(localMethod).HandlerFunc(f) + } } } - r.Methods("OPTIONS").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if srv.enableCors { - writeCorsHeaders(w, r) - } - w.WriteHeader(http.StatusOK) - }) return r, nil } From 3ea6a2c7c39695fbfe3dfe228db00335c9937c14 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 11 Jun 2013 10:17:39 +0000 Subject: [PATCH 13/13] add Michael Crosby to AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 1599a1d0e..eb90cd02d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -42,6 +42,7 @@ Ken Cochrane Kevin J. Lynagh Louis Opter Maxim Treskin +Michael Crosby Mikhail Sobolev Nate Jones Nelson Chen