aboutsummaryrefslogtreecommitdiff
path: root/pkg/adapter/containers_remote.go
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2019-03-22 13:32:48 -0500
committerbaude <bbaude@redhat.com>2019-04-10 08:59:28 -0500
commitfbcda7772d9fb7667be3a26fbabea0a7b5ea9a58 (patch)
treebe81fbb0543dd51fa9c532f9ec5127c508a1901f /pkg/adapter/containers_remote.go
parent2f2c7660c3a30d4c28c03eeeba8edc39f7864c7a (diff)
downloadpodman-fbcda7772d9fb7667be3a26fbabea0a7b5ea9a58.tar.gz
podman-fbcda7772d9fb7667be3a26fbabea0a7b5ea9a58.tar.bz2
podman-fbcda7772d9fb7667be3a26fbabea0a7b5ea9a58.zip
Add the ability to attach remotely to a container
Also, you can now podman-remote run -it. There are some bugs that need to be ironed out but I would prefer to merge this so we can make both progress on start and exec as well as the bugs. * when doing podman-remote run -it foo /bin/bash, you have to press enter to get the prompt to display. with the localized podman, we had to teach it connect to the console first and then start the container so we did not miss anything. * when executing "exit" in the console, we get a hard lockup likely because nobody knows what to do. * custom detach keys are not supported * podman-remote run -it alpine ls does not currently work. only dropping to a shell works. Signed-off-by: baude <bbaude@redhat.com>
Diffstat (limited to 'pkg/adapter/containers_remote.go')
-rw-r--r--pkg/adapter/containers_remote.go152
1 files changed, 140 insertions, 12 deletions
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 424c431df..1ae39749f 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -6,19 +6,25 @@ import (
"context"
"encoding/json"
"fmt"
+ "io"
+ "os"
"strconv"
"syscall"
"time"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-
iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/inspect"
+ "github.com/containers/libpod/pkg/varlinkapi/virtwriter"
+ "github.com/docker/docker/pkg/term"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/varlink/go/varlink"
+ "golang.org/x/crypto/ssh/terminal"
+ "k8s.io/client-go/tools/remotecommand"
)
// Inspect returns an inspect struct from varlink
@@ -71,6 +77,19 @@ func (r *LocalRuntime) ContainerState(name string) (*libpod.ContainerState, erro
}
+// Spec obtains the container spec.
+func (r *LocalRuntime) Spec(name string) (*specs.Spec, error) {
+ reply, err := iopodman.Spec().Call(r.Conn, name)
+ if err != nil {
+ return nil, err
+ }
+ data := specs.Spec{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
+ }
+ return &data, nil
+}
+
// LookupContainer gets basic information about container over a varlink
// connection and then translates it to a *Container
func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) {
@@ -79,10 +98,6 @@ func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) {
return nil, err
}
config := r.Config(idOrName)
- if err != nil {
- return nil, err
- }
-
return &Container{
remoteContainer{
r,
@@ -322,18 +337,32 @@ func (r *LocalRuntime) CreateContainer(ctx context.Context, c *cliconfig.CreateV
// Run creates a container overvarlink and then starts it
func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode int) (int, error) {
+ // FIXME
+ // podman-remote run -it alpine ls DOES NOT WORK YET
+ // podman-remote run -it alpine /bin/sh does, i suspect there is some sort of
+ // timing issue between the socket availability and terminal setup and the command
+ // being run.
+
// TODO the exit codes for run need to be figured out for remote connections
- if !c.Bool("detach") {
- return 0, errors.New("the remote client only supports detached containers")
- }
results := shared.NewIntermediateLayer(&c.PodmanCommand)
cid, err := iopodman.CreateContainer().Call(r.Conn, results.MakeVarlink())
if err != nil {
return 0, err
}
- fmt.Println(cid)
_, err = iopodman.StartContainer().Call(r.Conn, cid)
- return 0, err
+ if err != nil {
+ return 0, err
+ }
+ errChan, err := r.attach(ctx, os.Stdin, os.Stdout, cid)
+ if err != nil {
+ return 0, err
+ }
+ if c.Bool("detach") {
+ fmt.Println(cid)
+ return 0, err
+ }
+ finalError := <-errChan
+ return 0, finalError
}
func ReadExitFile(runtimeTmp, ctrID string) (int, error) {
@@ -411,3 +440,102 @@ func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]share
}
return psContainers, nil
}
+
+func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid string) (chan error, error) {
+ var (
+ oldTermState *term.State
+ )
+ errChan := make(chan error)
+ spec, err := r.Spec(cid)
+ if err != nil {
+ return nil, err
+ }
+ resize := make(chan remotecommand.TerminalSize)
+ haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
+
+ // Check if we are attached to a terminal. If we are, generate resize
+ // events, and set the terminal to raw mode
+ if haveTerminal && spec.Process.Terminal {
+ logrus.Debugf("Handling terminal attach")
+
+ subCtx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ resizeTty(subCtx, resize)
+ oldTermState, err = term.SaveState(os.Stdin.Fd())
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to save terminal state")
+ }
+
+ logrus.SetFormatter(&RawTtyFormatter{})
+ term.SetRawTerminal(os.Stdin.Fd())
+
+ }
+
+ _, err = iopodman.Attach().Send(r.Conn, varlink.Upgrade, cid)
+ if err != nil {
+ restoreTerminal(oldTermState)
+ return nil, err
+ }
+
+ // These are the varlink sockets
+ reader := r.Conn.Reader
+ writer := r.Conn.Writer
+
+ // These are the special writers that encode input from the client.
+ varlinkStdinWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdin)
+ varlinkResizeWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.TerminalResize)
+
+ go func() {
+ // Read from the wire and direct to stdout or stderr
+ err := virtwriter.Reader(reader, stdout, os.Stderr, nil, nil)
+ defer restoreTerminal(oldTermState)
+ errChan <- err
+ }()
+
+ go func() {
+ for termResize := range resize {
+ b, err := json.Marshal(termResize)
+ if err != nil {
+ defer restoreTerminal(oldTermState)
+ errChan <- err
+ }
+ _, err = varlinkResizeWriter.Write(b)
+ if err != nil {
+ defer restoreTerminal(oldTermState)
+ errChan <- err
+ }
+ }
+ }()
+
+ // Takes stdinput and sends it over the wire after being encoded
+ go func() {
+ if _, err := io.Copy(varlinkStdinWriter, stdin); err != nil {
+ defer restoreTerminal(oldTermState)
+ errChan <- err
+ }
+
+ }()
+ return errChan, nil
+
+}
+
+// Attach to a remote terminal
+func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) error {
+ ctr, err := r.LookupContainer(c.InputArgs[0])
+ if err != nil {
+ return nil
+ }
+ if ctr.state.State != libpod.ContainerStateRunning {
+ return errors.New("you can only attach to running containers")
+ }
+ inputStream := os.Stdin
+ if c.NoStdin {
+ inputStream = nil
+ }
+ errChan, err := r.attach(ctx, inputStream, os.Stdout, c.InputArgs[0])
+ if err != nil {
+ return err
+ }
+ return <-errChan
+}