diff options
Diffstat (limited to 'pkg/adapter')
-rw-r--r-- | pkg/adapter/containers.go | 130 | ||||
-rw-r--r-- | pkg/adapter/containers_remote.go | 42 | ||||
-rw-r--r-- | pkg/adapter/terminal_linux.go | 57 |
3 files changed, 155 insertions, 74 deletions
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 9726b237f..47f1b091e 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -924,13 +924,85 @@ func (r *LocalRuntime) execPS(c *libpod.Container, args []string) ([]string, err }() cmd := append([]string{"ps"}, args...) - if err := c.Exec(false, false, []string{}, cmd, "", "", streams, 0); err != nil { + ec, err := c.Exec(false, false, []string{}, cmd, "", "", streams, 0, nil, "") + if err != nil { return nil, err + } else if ec != 0 { + return nil, errors.Errorf("Runtime failed with exit status: %d and output: %s", ec, strings.Join(psOutput, " ")) } return psOutput, nil } +// ExecContainer executes a command in the container +func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecValues) (int, error) { + var ( + ctr *Container + err error + cmd []string + ) + // default invalid command exit code + ec := 125 + + if cli.Latest { + if ctr, err = r.GetLatestContainer(); err != nil { + return ec, err + } + cmd = cli.InputArgs[0:] + } else { + if ctr, err = r.LookupContainer(cli.InputArgs[0]); err != nil { + return ec, err + } + cmd = cli.InputArgs[1:] + } + + if cli.PreserveFDs > 0 { + entries, err := ioutil.ReadDir("/proc/self/fd") + if err != nil { + return ec, errors.Wrapf(err, "unable to read /proc/self/fd") + } + + m := make(map[int]bool) + for _, e := range entries { + i, err := strconv.Atoi(e.Name()) + if err != nil { + return ec, errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name()) + } + m[i] = true + } + + for i := 3; i < 3+cli.PreserveFDs; i++ { + if _, found := m[i]; !found { + return ec, errors.New("invalid --preserve-fds=N specified. Not enough FDs available") + } + } + } + + // Validate given environment variables + env := map[string]string{} + if err := parse.ReadKVStrings(env, []string{}, cli.Env); err != nil { + return ec, errors.Wrapf(err, "unable to process environment variables") + } + + // Build env slice of key=value strings for Exec + envs := []string{} + for k, v := range env { + envs = append(envs, fmt.Sprintf("%s=%s", k, v)) + } + + streams := new(libpod.AttachStreams) + streams.OutputStream = os.Stdout + streams.ErrorStream = os.Stderr + if cli.Interactive { + streams.InputStream = os.Stdin + streams.AttachInput = true + } + streams.AttachOutput = true + streams.AttachError = true + + return ExecAttachCtr(ctx, ctr.Container, cli.Tty, cli.Privileged, envs, cmd, cli.User, cli.Workdir, streams, cli.PreserveFDs, cli.DetachKeys) +} + // Prune removes stopped containers func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool) ([]string, map[string]error, error) { var ( @@ -1129,59 +1201,3 @@ func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, co } return newImage.ID(), nil } - -// Exec a command in a container -func (r *LocalRuntime) Exec(c *cliconfig.ExecValues, cmd []string) error { - var ctr *Container - var err error - - if c.Latest { - ctr, err = r.GetLatestContainer() - } else { - ctr, err = r.LookupContainer(c.InputArgs[0]) - } - if err != nil { - return errors.Wrapf(err, "unable to exec into %s", c.InputArgs[0]) - } - - if c.PreserveFDs > 0 { - entries, err := ioutil.ReadDir("/proc/self/fd") - if err != nil { - return errors.Wrapf(err, "unable to read /proc/self/fd") - } - m := make(map[int]bool) - for _, e := range entries { - i, err := strconv.Atoi(e.Name()) - if err != nil { - return errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name()) - } - m[i] = true - } - for i := 3; i < 3+c.PreserveFDs; i++ { - if _, found := m[i]; !found { - return errors.New("invalid --preserve-fds=N specified. Not enough FDs available") - } - } - } - - // ENVIRONMENT VARIABLES - env := map[string]string{} - - if err := parse.ReadKVStrings(env, []string{}, c.Env); err != nil { - return errors.Wrapf(err, "unable to process environment variables") - } - envs := []string{} - for k, v := range env { - envs = append(envs, fmt.Sprintf("%s=%s", k, v)) - } - - streams := new(libpod.AttachStreams) - streams.OutputStream = os.Stdout - streams.ErrorStream = os.Stderr - streams.InputStream = os.Stdin - streams.AttachOutput = true - streams.AttachError = true - streams.AttachInput = true - - return ctr.Exec(c.Tty, c.Privileged, envs, cmd, c.User, c.Workdir, streams, c.PreserveFDs) -} diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index fc23381a4..6b9fc8ee7 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -14,6 +14,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/cmd/podman/shared/parse" iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" @@ -1034,7 +1035,42 @@ func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, co return iid, nil } -// Exec executes a container in a running container -func (r *LocalRuntime) Exec(c *cliconfig.ExecValues, cmd []string) error { - return define.ErrNotImplemented +// ExecContainer executes a command in the container +func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecValues) (int, error) { + // default invalid command exit code + ec := 125 + // Validate given environment variables + env := map[string]string{} + if err := parse.ReadKVStrings(env, []string{}, cli.Env); err != nil { + return -1, errors.Wrapf(err, "Exec unable to process environment variables") + } + + // Build env slice of key=value strings for Exec + envs := []string{} + for k, v := range env { + envs = append(envs, fmt.Sprintf("%s=%s", k, v)) + } + + opts := iopodman.ExecOpts{ + Name: cli.InputArgs[0], + Tty: cli.Tty, + Privileged: cli.Privileged, + Cmd: cli.InputArgs[1:], + User: &cli.User, + Workdir: &cli.Workdir, + Env: &envs, + } + + receive, err := iopodman.ExecContainer().Send(r.Conn, varlink.Upgrade, opts) + if err != nil { + return ec, errors.Wrapf(err, "Exec failed to contact service for %s", cli.InputArgs) + } + + _, err = receive() + if err != nil { + return ec, errors.Wrapf(err, "Exec operation failed for %s", cli.InputArgs) + } + + // TODO return exit code from exec call + return 0, nil } diff --git a/pkg/adapter/terminal_linux.go b/pkg/adapter/terminal_linux.go index 533a8baf5..33ca0833b 100644 --- a/pkg/adapter/terminal_linux.go +++ b/pkg/adapter/terminal_linux.go @@ -13,6 +13,25 @@ import ( "k8s.io/client-go/tools/remotecommand" ) +// ExecAttachCtr execs and attaches to a container +func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env, cmd []string, user, workDir string, streams *libpod.AttachStreams, preserveFDs int, detachKeys string) (int, error) { + 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 && tty { + cancel, oldTermState, err := handleTerminalAttach(ctx, resize) + if err != nil { + return -1, err + } + defer cancel() + defer restoreTerminal(oldTermState) + } + return ctr.Exec(tty, privileged, env, cmd, user, workDir, streams, preserveFDs, resize, detachKeys) +} + // StartAttachCtr starts and (if required) attaches to a container // if you change the signature of this function from os.File to io.Writer, it will trigger a downstream // error. we may need to just lint disable this one. @@ -24,28 +43,16 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, // Check if we are attached to a terminal. If we are, generate resize // events, and set the terminal to raw mode if haveTerminal && ctr.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()) + cancel, oldTermState, err := handleTerminalAttach(ctx, resize) if err != nil { - return errors.Wrapf(err, "unable to save terminal state") - } - - logrus.SetFormatter(&RawTtyFormatter{}) - if _, err := term.SetRawTerminal(os.Stdin.Fd()); err != nil { return err } - defer func() { if err := restoreTerminal(oldTermState); err != nil { logrus.Errorf("unable to restore terminal: %q", err) } }() + defer cancel() } streams := new(libpod.AttachStreams) @@ -97,3 +104,25 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, return nil } + +func handleTerminalAttach(ctx context.Context, resize chan remotecommand.TerminalSize) (context.CancelFunc, *term.State, error) { + logrus.Debugf("Handling terminal attach") + + subCtx, cancel := context.WithCancel(ctx) + + resizeTty(subCtx, resize) + + oldTermState, err := term.SaveState(os.Stdin.Fd()) + if err != nil { + // allow caller to not have to do any cleaning up if we error here + cancel() + return nil, nil, errors.Wrapf(err, "unable to save terminal state") + } + + logrus.SetFormatter(&RawTtyFormatter{}) + if _, err := term.SetRawTerminal(os.Stdin.Fd()); err != nil { + return nil, nil, err + } + + return cancel, oldTermState, nil +} |