diff options
Diffstat (limited to 'pkg/api/handlers/compat/containers_attach.go')
-rw-r--r-- | pkg/api/handlers/compat/containers_attach.go | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go new file mode 100644 index 000000000..da7b5bb0c --- /dev/null +++ b/pkg/api/handlers/compat/containers_attach.go @@ -0,0 +1,156 @@ +package compat + +import ( + "net/http" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/gorilla/schema" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "k8s.io/client-go/tools/remotecommand" +) + +func AttachContainer(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value("decoder").(*schema.Decoder) + + query := struct { + DetachKeys string `schema:"detachKeys"` + Logs bool `schema:"logs"` + Stream bool `schema:"stream"` + Stdin bool `schema:"stdin"` + Stdout bool `schema:"stdout"` + Stderr bool `schema:"stderr"` + }{} + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, "Error parsing parameters", http.StatusBadRequest, err) + return + } + + // Detach keys: explicitly set to "" is very different from unset + // TODO: Our format for parsing these may be different from Docker. + var detachKeys *string + if _, found := r.URL.Query()["detachKeys"]; found { + detachKeys = &query.DetachKeys + } + + streams := new(libpod.HTTPAttachStreams) + streams.Stdout = true + streams.Stderr = true + streams.Stdin = true + useStreams := false + if _, found := r.URL.Query()["stdin"]; found { + streams.Stdin = query.Stdin + useStreams = true + } + if _, found := r.URL.Query()["stdout"]; found { + streams.Stdout = query.Stdout + useStreams = true + } + if _, found := r.URL.Query()["stderr"]; found { + streams.Stderr = query.Stderr + useStreams = true + } + if !useStreams { + streams = nil + } + if useStreams && !streams.Stdout && !streams.Stderr && !streams.Stdin { + utils.Error(w, "Parameter conflict", http.StatusBadRequest, errors.Errorf("at least one of stdin, stdout, stderr must be true")) + return + } + + // TODO: Investigate supporting these. + // Logs replays container logs over the attach socket. + // Stream seems to break things up somehow? Not 100% clear. + if query.Logs { + utils.Error(w, "Unsupported parameter", http.StatusBadRequest, errors.Errorf("the logs parameter to attach is not presently supported")) + return + } + // We only support stream=true or unset + if _, found := r.URL.Query()["stream"]; found && query.Stream { + utils.Error(w, "Unsupported parameter", http.StatusBadRequest, errors.Errorf("the stream parameter to attach is not presently supported")) + return + } + + name := utils.GetName(r) + ctr, err := runtime.LookupContainer(name) + if err != nil { + utils.ContainerNotFound(w, name, err) + return + } + + state, err := ctr.State() + if err != nil { + utils.InternalServerError(w, err) + return + } + if !(state == define.ContainerStateCreated || state == define.ContainerStateRunning) { + utils.InternalServerError(w, errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers")) + return + } + + // Hijack the connection + hijacker, ok := w.(http.Hijacker) + if !ok { + utils.InternalServerError(w, errors.Errorf("unable to hijack connection")) + return + } + + w.WriteHeader(http.StatusSwitchingProtocols) + + connection, buffer, err := hijacker.Hijack() + if err != nil { + utils.InternalServerError(w, errors.Wrapf(err, "error hijacking connection")) + return + } + + logrus.Debugf("Hijack for attach of container %s successful", ctr.ID()) + + // Perform HTTP attach. + // HTTPAttach will handle everything about the connection from here on + // (including closing it and writing errors to it). + if err := ctr.HTTPAttach(connection, buffer, streams, detachKeys, nil); err != nil { + // We can't really do anything about errors anymore. HTTPAttach + // should be writing them to the connection. + logrus.Errorf("Error attaching to container %s: %v", ctr.ID(), err) + } + + logrus.Debugf("Attach for container %s completed successfully", ctr.ID()) +} + +func ResizeContainer(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value("decoder").(*schema.Decoder) + + query := struct { + Height uint16 `schema:"h"` + Width uint16 `schema:"w"` + }{} + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + // This is not a 400, despite the fact that is should be, for + // compatibility reasons. + utils.InternalServerError(w, errors.Wrapf(err, "error parsing query options")) + return + } + + name := utils.GetName(r) + ctr, err := runtime.LookupContainer(name) + if err != nil { + utils.ContainerNotFound(w, name, err) + return + } + + newSize := remotecommand.TerminalSize{ + Width: query.Width, + Height: query.Height, + } + if err := ctr.AttachResize(newSize); err != nil { + utils.InternalServerError(w, err) + return + } + // This is not a 204, even though we write nothing, for compatibility + // reasons. + utils.WriteResponse(w, http.StatusOK, "") +} |