Implement non-terminal attach
We use a SOCK_SEQPACKET socket for the attach unix domain socket, which means the kernel will ensure that the reading side only ever get the data from one write operation. We use this for frameing, where the first byte is the pipe that the next bytes are for. We have to make sure that all reads from the socket are using at least the same size of buffer as the write side, because otherwise the extra data in the message will be dropped. This also adds a stdin pipe for the container, similar to the ones we use for stdout/err, because we need a way for an attached client to write to stdin, even if not using a tty. This fixes https://github.com/kubernetes-incubator/cri-o/issues/569 Signed-off-by: Alexander Larsson <alexl@redhat.com>
This commit is contained in:
parent
e3170caa2e
commit
7bb957bf75
8 changed files with 143 additions and 42 deletions
|
@ -3,14 +3,12 @@ package server
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/pools"
|
||||
"github.com/kubernetes-incubator/cri-o/oci"
|
||||
"github.com/kubernetes-incubator/cri-o/utils"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -19,6 +17,13 @@ import (
|
|||
"k8s.io/kubernetes/pkg/util/term"
|
||||
)
|
||||
|
||||
/* Sync with stdpipe_t in conmon.c */
|
||||
const (
|
||||
AttachPipeStdin = 1
|
||||
AttachPipeStdout = 2
|
||||
AttachPipeStderr = 3
|
||||
)
|
||||
|
||||
// Attach prepares a streaming endpoint to attach to a running container.
|
||||
func (s *Server) Attach(ctx context.Context, req *pb.AttachRequest) (*pb.AttachResponse, error) {
|
||||
logrus.Debugf("AttachRequest %+v", req)
|
||||
|
@ -63,7 +68,7 @@ func (ss streamService) Attach(containerID string, inputStream io.Reader, output
|
|||
})
|
||||
|
||||
attachSocketPath := filepath.Join("/var/run/crio", c.ID(), "attach")
|
||||
conn, err := net.Dial("unix", attachSocketPath)
|
||||
conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: attachSocketPath, Net: "unixpacket"})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to container %s attach socket: %v", c.ID(), err)
|
||||
}
|
||||
|
@ -72,7 +77,7 @@ func (ss streamService) Attach(containerID string, inputStream io.Reader, output
|
|||
receiveStdout := make(chan error)
|
||||
if outputStream != nil || errorStream != nil {
|
||||
go func() {
|
||||
receiveStdout <- redirectResponseToOutputStream(tty, outputStream, errorStream, conn)
|
||||
receiveStdout <- redirectResponseToOutputStreams(outputStream, errorStream, conn)
|
||||
}()
|
||||
}
|
||||
|
||||
|
@ -81,6 +86,7 @@ func (ss streamService) Attach(containerID string, inputStream io.Reader, output
|
|||
var err error
|
||||
if inputStream != nil {
|
||||
_, err = utils.CopyDetachable(conn, inputStream, nil)
|
||||
conn.CloseWrite()
|
||||
}
|
||||
stdinDone <- err
|
||||
}()
|
||||
|
@ -100,18 +106,42 @@ func (ss streamService) Attach(containerID string, inputStream io.Reader, output
|
|||
return nil
|
||||
}
|
||||
|
||||
func redirectResponseToOutputStream(tty bool, outputStream, errorStream io.Writer, conn io.Reader) error {
|
||||
if outputStream == nil {
|
||||
outputStream = ioutil.Discard
|
||||
}
|
||||
if errorStream == nil {
|
||||
// errorStream = ioutil.Discard
|
||||
}
|
||||
func redirectResponseToOutputStreams(outputStream, errorStream io.Writer, conn io.Reader) error {
|
||||
var err error
|
||||
if tty {
|
||||
_, err = pools.Copy(outputStream, conn)
|
||||
} else {
|
||||
// TODO
|
||||
buf := make([]byte, 8192+1) /* Sync with conmon STDIO_BUF_SIZE */
|
||||
|
||||
for {
|
||||
nr, er := conn.Read(buf)
|
||||
if nr > 0 {
|
||||
var dst io.Writer
|
||||
if buf[0] == AttachPipeStdout {
|
||||
dst = outputStream
|
||||
} else if buf[0] == AttachPipeStderr {
|
||||
dst = errorStream
|
||||
} else {
|
||||
logrus.Infof("Got unexpected attach type %+d", buf[0])
|
||||
}
|
||||
|
||||
if dst != nil {
|
||||
nw, ew := dst.Write(buf[1:nr])
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
if nr != nw+1 {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if er == io.EOF {
|
||||
break
|
||||
}
|
||||
if er != nil {
|
||||
err = er
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue