From 2365ec79e628c010b9fc2391a7f4f3f0ad6920ec Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 22 Dec 2014 23:17:53 +0000 Subject: [PATCH] attach support Signed-off-by: Victor Vieux --- api/api.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/api/api.go b/api/api.go index 4ad2365ec9..3c73b607d0 100644 --- a/api/api.go +++ b/api/api.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "io/ioutil" + "net" "net/http" "runtime" "sort" @@ -136,11 +137,6 @@ func postContainersCreate(c *context, w http.ResponseWriter, r *http.Request) { return } - if config.AttachStdout || config.AttachStdin || config.AttachStderr { - httpError(w, "Attach is not supported in clustering mode, use -d.", http.StatusInternalServerError) - return - } - if container := c.cluster.Container(name); container != nil { httpError(w, fmt.Sprintf("Conflict, The name %s is already assigned to %s. You have to delete (or rename) that container to be able to assign %s to a container again.", name, container.Id, name), http.StatusConflict) return @@ -225,6 +221,52 @@ func proxyContainer(c *context, w http.ResponseWriter, r *http.Request) { } } +// Proxy a hijack request to the right node +func proxyHijack(c *context, w http.ResponseWriter, r *http.Request) { + container := c.cluster.Container(mux.Vars(r)["name"]) + if container != nil { + addr := container.Node().Addr + if parts := strings.SplitN(container.Node().Addr, "://", 2); len(parts) == 2 { + addr = parts[1] + } + + log.Debugf("[HIJACK PROXY] --> %s", addr) + + d, err := net.Dial("tcp", addr) + if err != nil { + httpError(w, err.Error(), http.StatusInternalServerError) + return + } + hj, ok := w.(http.Hijacker) + if !ok { + httpError(w, err.Error(), http.StatusInternalServerError) + return + } + nc, _, err := hj.Hijack() + if err != nil { + httpError(w, err.Error(), http.StatusInternalServerError) + return + } + defer nc.Close() + defer d.Close() + + err = r.Write(d) + if err != nil { + httpError(w, err.Error(), http.StatusInternalServerError) + return + } + + errc := make(chan error, 2) + cp := func(dst io.Writer, src io.Reader) { + _, err := io.Copy(dst, src) + errc <- err + } + go cp(d, nc) + go cp(nc, d) + <-errc + } +} + // Default handler for methods not supported by clustering. func notImplementedHandler(c *context, w http.ResponseWriter, r *http.Request) { httpError(w, "Not supported in clustering mode.", http.StatusNotImplemented) @@ -287,7 +329,7 @@ func createRouter(c *context, enableCors bool) (*mux.Router, error) { "/containers/{name:.*}/stop": proxyContainer, "/containers/{name:.*}/wait": proxyContainer, "/containers/{name:.*}/resize": proxyContainer, - "/containers/{name:.*}/attach": notImplementedHandler, + "/containers/{name:.*}/attach": proxyHijack, "/containers/{name:.*}/copy": notImplementedHandler, "/containers/{name:.*}/exec": notImplementedHandler, "/exec/{name:.*}/start": notImplementedHandler,