aboutsummaryrefslogtreecommitdiff
path: root/libpod/container_api.go
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@pm.me>2020-01-10 13:37:10 -0500
committerMatthew Heon <matthew.heon@pm.me>2020-01-16 13:49:21 -0500
commitac47e80b07ddc1e56e7c4fd6b0deca9f3bdc5f54 (patch)
tree7d5f49fde60dd3b9299991e248d3eb37f756d73a /libpod/container_api.go
parentdb00ee97e950290a6bc5d669cde0cbc54bb94afe (diff)
downloadpodman-ac47e80b07ddc1e56e7c4fd6b0deca9f3bdc5f54.tar.gz
podman-ac47e80b07ddc1e56e7c4fd6b0deca9f3bdc5f54.tar.bz2
podman-ac47e80b07ddc1e56e7c4fd6b0deca9f3bdc5f54.zip
Add an API for Attach over HTTP API
The new APIv2 branch provides an HTTP-based remote API to Podman. The requirements of this are, unfortunately, incompatible with the existing Attach API. For non-terminal attach, we need append a header to what was copied from the container, to multiplex STDOUT and STDERR; to do this with the old API, we'd need to copy into an intermediate buffer first, to handle the headers. To avoid this, provide a new API to handle all aspects of terminal and non-terminal attach, including closing the hijacked HTTP connection. This might be a bit too specific, but for now, it seems to be the simplest approach. At the same time, add a Resize endpoint. This needs to be a separate endpoint, so our existing channel approach does not work here. I wanted to rework the rest of attach at the same time (some parts of it, particularly how we start the Attach session and how we do resizing, are (in my opinion) handled much better here. That may still be on the table, but I wanted to avoid breaking existing APIs in this already massive change. Signed-off-by: Matthew Heon <matthew.heon@pm.me>
Diffstat (limited to 'libpod/container_api.go')
-rw-r--r--libpod/container_api.go69
1 files changed, 67 insertions, 2 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go
index e36623529..d74a14f15 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -5,6 +5,7 @@ import (
"context"
"io"
"io/ioutil"
+ "net"
"os"
"time"
@@ -374,7 +375,9 @@ type AttachStreams struct {
AttachInput bool
}
-// Attach attaches to a container
+// Attach attaches to a container.
+// This function returns when the attach finishes. It does not hold the lock for
+// the duration of its runtime, only using it at the beginning to verify state.
func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize) error {
if !c.batched {
c.lock.Lock()
@@ -382,6 +385,7 @@ func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan re
c.lock.Unlock()
return err
}
+ // We are NOT holding the lock for the duration of the function.
c.lock.Unlock()
}
@@ -389,10 +393,71 @@ func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan re
return errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers")
}
- defer c.newContainerEvent(events.Attach)
+ c.newContainerEvent(events.Attach)
return c.attach(streams, keys, resize, false, nil)
}
+// HTTPAttach forwards an attach session over a hijacked HTTP session.
+// HTTPAttach will consume and close the included httpCon, which is expected to
+// be sourced from a hijacked HTTP connection.
+// The cancel channel is optional, and can be used to asyncronously cancel the
+// attach session.
+// The streams variable is only supported if the container was not a terminal,
+// and allows specifying which of the container's standard streams will be
+// forwarded to the client.
+// This function returns when the attach finishes. It does not hold the lock for
+// the duration of its runtime, only using it at the beginning to verify state.
+func (c *Container) HTTPAttach(httpCon net.Conn, httpBuf *bufio.ReadWriter, streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool) error {
+ if !c.batched {
+ c.lock.Lock()
+ if err := c.syncContainer(); err != nil {
+ c.lock.Unlock()
+
+ // Write any errors to the HTTP buffer before we close.
+ hijackWriteErrorAndClose(err, c.ID(), httpCon, httpBuf)
+
+ return err
+ }
+ // We are NOT holding the lock for the duration of the function.
+ c.lock.Unlock()
+ }
+
+ if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) {
+ toReturn := errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers")
+
+ // Write any errors to the HTTP buffer before we close.
+ hijackWriteErrorAndClose(toReturn, c.ID(), httpCon, httpBuf)
+
+ return toReturn
+ }
+
+ logrus.Infof("Performing HTTP Hijack attach to container %s", c.ID())
+
+ c.newContainerEvent(events.Attach)
+ return c.ociRuntime.HTTPAttach(c, httpCon, httpBuf, streams, detachKeys, cancel)
+}
+
+// AttachResize resizes the container's terminal, which is displayed by Attach
+// and HTTPAttach.
+func (c *Container) AttachResize(newSize remotecommand.TerminalSize) error {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return err
+ }
+ }
+
+ if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) {
+ return errors.Wrapf(define.ErrCtrStateInvalid, "can only resize created or running containers")
+ }
+
+ logrus.Infof("Resizing TTY of container %s", c.ID())
+
+ return c.ociRuntime.AttachResize(c, newSize)
+}
+
// Mount mounts a container's filesystem on the host
// The path where the container has been mounted is returned
func (c *Container) Mount() (string, error) {