mirror of https://github.com/containers/podman.git
Add CLI frontend for detached exec
Add a new ContainerEngine method for creating a detached exec session, and wire in the frontend code to do this. As part of this, move Streams out of ExecOptions to the function signature in an effort to share the struct between both methods. Fixes #5884 Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
parent
43413887c0
commit
05a034118f
|
@ -2,9 +2,11 @@ package containers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containers/libpod/cmd/podman/registry"
|
"github.com/containers/libpod/cmd/podman/registry"
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
envLib "github.com/containers/libpod/pkg/env"
|
envLib "github.com/containers/libpod/pkg/env"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -41,10 +43,12 @@ var (
|
||||||
var (
|
var (
|
||||||
envInput, envFile []string
|
envInput, envFile []string
|
||||||
execOpts entities.ExecOptions
|
execOpts entities.ExecOptions
|
||||||
|
execDetach bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func execFlags(flags *pflag.FlagSet) {
|
func execFlags(flags *pflag.FlagSet) {
|
||||||
flags.SetInterspersed(false)
|
flags.SetInterspersed(false)
|
||||||
|
flags.BoolVarP(&execDetach, "detach", "d", false, "Run the exec session in detached mode (backgrounded)")
|
||||||
flags.StringVar(&execOpts.DetachKeys, "detach-keys", containerConfig.DetachKeys(), "Select the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _")
|
flags.StringVar(&execOpts.DetachKeys, "detach-keys", containerConfig.DetachKeys(), "Select the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _")
|
||||||
flags.StringArrayVarP(&envInput, "env", "e", []string{}, "Set environment variables")
|
flags.StringArrayVarP(&envInput, "env", "e", []string{}, "Set environment variables")
|
||||||
flags.StringSliceVar(&envFile, "env-file", []string{}, "Read in a file of environment variables")
|
flags.StringSliceVar(&envFile, "env-file", []string{}, "Read in a file of environment variables")
|
||||||
|
@ -106,16 +110,27 @@ func exec(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
execOpts.Envs = envLib.Join(execOpts.Envs, cliEnv)
|
execOpts.Envs = envLib.Join(execOpts.Envs, cliEnv)
|
||||||
execOpts.Streams.OutputStream = os.Stdout
|
|
||||||
execOpts.Streams.ErrorStream = os.Stderr
|
|
||||||
if execOpts.Interactive {
|
|
||||||
execOpts.Streams.InputStream = bufio.NewReader(os.Stdin)
|
|
||||||
execOpts.Streams.AttachInput = true
|
|
||||||
}
|
|
||||||
execOpts.Streams.AttachOutput = true
|
|
||||||
execOpts.Streams.AttachError = true
|
|
||||||
|
|
||||||
exitCode, err := registry.ContainerEngine().ContainerExec(registry.GetContext(), nameOrId, execOpts)
|
if !execDetach {
|
||||||
registry.SetExitCode(exitCode)
|
streams := define.AttachStreams{}
|
||||||
return err
|
streams.OutputStream = os.Stdout
|
||||||
|
streams.ErrorStream = os.Stderr
|
||||||
|
if execOpts.Interactive {
|
||||||
|
streams.InputStream = bufio.NewReader(os.Stdin)
|
||||||
|
streams.AttachInput = true
|
||||||
|
}
|
||||||
|
streams.AttachOutput = true
|
||||||
|
streams.AttachError = true
|
||||||
|
|
||||||
|
exitCode, err := registry.ContainerEngine().ContainerExec(registry.GetContext(), nameOrId, execOpts, streams)
|
||||||
|
registry.SetExitCode(exitCode)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := registry.ContainerEngine().ContainerExecDetached(registry.GetContext(), nameOrId, execOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(id)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,7 +242,6 @@ type ExecOptions struct {
|
||||||
Latest bool
|
Latest bool
|
||||||
PreserveFDs uint
|
PreserveFDs uint
|
||||||
Privileged bool
|
Privileged bool
|
||||||
Streams define.AttachStreams
|
|
||||||
Tty bool
|
Tty bool
|
||||||
User string
|
User string
|
||||||
WorkDir string
|
WorkDir string
|
||||||
|
|
|
@ -19,7 +19,8 @@ type ContainerEngine interface {
|
||||||
ContainerCp(ctx context.Context, source, dest string, options ContainerCpOptions) (*ContainerCpReport, error)
|
ContainerCp(ctx context.Context, source, dest string, options ContainerCpOptions) (*ContainerCpReport, error)
|
||||||
ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
|
ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
|
||||||
ContainerDiff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error)
|
ContainerDiff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error)
|
||||||
ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error)
|
ContainerExec(ctx context.Context, nameOrId string, options ExecOptions, streams define.AttachStreams) (int, error)
|
||||||
|
ContainerExecDetached(ctx context.Context, nameOrID string, options ExecOptions) (string, error)
|
||||||
ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error)
|
ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error)
|
||||||
ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error
|
ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error
|
||||||
ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error)
|
ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error)
|
||||||
|
|
|
@ -536,7 +536,22 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) {
|
func makeExecConfig(options entities.ExecOptions) *libpod.ExecConfig {
|
||||||
|
execConfig := new(libpod.ExecConfig)
|
||||||
|
execConfig.Command = options.Cmd
|
||||||
|
execConfig.Terminal = options.Tty
|
||||||
|
execConfig.Privileged = options.Privileged
|
||||||
|
execConfig.Environment = options.Envs
|
||||||
|
execConfig.User = options.User
|
||||||
|
execConfig.WorkDir = options.WorkDir
|
||||||
|
execConfig.DetachKeys = &options.DetachKeys
|
||||||
|
execConfig.PreserveFDs = options.PreserveFDs
|
||||||
|
execConfig.AttachStdin = options.Interactive
|
||||||
|
|
||||||
|
return execConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkExecPreserveFDs(options entities.ExecOptions) (int, error) {
|
||||||
ec := define.ExecErrorCodeGeneric
|
ec := define.ExecErrorCodeGeneric
|
||||||
if options.PreserveFDs > 0 {
|
if options.PreserveFDs > 0 {
|
||||||
entries, err := ioutil.ReadDir("/proc/self/fd")
|
entries, err := ioutil.ReadDir("/proc/self/fd")
|
||||||
|
@ -559,15 +574,52 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions, streams define.AttachStreams) (int, error) {
|
||||||
|
ec, err := checkExecPreserveFDs(options)
|
||||||
|
if err != nil {
|
||||||
|
return ec, err
|
||||||
|
}
|
||||||
ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod)
|
ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ec, err
|
return ec, err
|
||||||
}
|
}
|
||||||
ctr := ctrs[0]
|
ctr := ctrs[0]
|
||||||
ec, err = terminal.ExecAttachCtr(ctx, ctr, options.Tty, options.Privileged, options.Envs, options.Cmd, options.User, options.WorkDir, &options.Streams, options.PreserveFDs, options.DetachKeys)
|
|
||||||
|
execConfig := makeExecConfig(options)
|
||||||
|
|
||||||
|
ec, err = terminal.ExecAttachCtr(ctx, ctr, execConfig, &streams)
|
||||||
return define.TranslateExecErrorToExitCode(ec, err), err
|
return define.TranslateExecErrorToExitCode(ec, err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrId string, options entities.ExecOptions) (string, error) {
|
||||||
|
_, err := checkExecPreserveFDs(options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ctr := ctrs[0]
|
||||||
|
|
||||||
|
execConfig := makeExecConfig(options)
|
||||||
|
|
||||||
|
// Create and start the exec session
|
||||||
|
id, err := ctr.ExecCreate(execConfig)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: we should try and retrieve exit code if this fails.
|
||||||
|
if err := ctr.ExecStart(id); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
|
func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
|
||||||
var reports []*entities.ContainerStartReport
|
var reports []*entities.ContainerStartReport
|
||||||
var exitCode = define.ExecErrorCodeGeneric
|
var exitCode = define.ExecErrorCodeGeneric
|
||||||
|
|
|
@ -15,13 +15,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecAttachCtr execs and attaches to a container
|
// ExecAttachCtr execs and attaches to a container
|
||||||
func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
|
func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpod.ExecConfig, streams *define.AttachStreams) (int, error) {
|
||||||
resize := make(chan remotecommand.TerminalSize)
|
resize := make(chan remotecommand.TerminalSize)
|
||||||
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
|
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
|
||||||
|
|
||||||
// Check if we are attached to a terminal. If we are, generate resize
|
// Check if we are attached to a terminal. If we are, generate resize
|
||||||
// events, and set the terminal to raw mode
|
// events, and set the terminal to raw mode
|
||||||
if haveTerminal && tty {
|
if haveTerminal && execConfig.Terminal {
|
||||||
cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
|
cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
|
@ -34,16 +34,6 @@ func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged b
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
execConfig := new(libpod.ExecConfig)
|
|
||||||
execConfig.Command = cmd
|
|
||||||
execConfig.Terminal = tty
|
|
||||||
execConfig.Privileged = privileged
|
|
||||||
execConfig.Environment = env
|
|
||||||
execConfig.User = user
|
|
||||||
execConfig.WorkDir = workDir
|
|
||||||
execConfig.DetachKeys = &detachKeys
|
|
||||||
execConfig.PreserveFDs = preserveFDs
|
|
||||||
|
|
||||||
return ctr.Exec(execConfig, streams, resize)
|
return ctr.Exec(execConfig, streams, resize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -329,10 +329,14 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string,
|
||||||
return containers.Attach(ic.ClientCxt, nameOrId, &options.DetachKeys, nil, bindings.PTrue, options.Stdin, options.Stdout, options.Stderr)
|
return containers.Attach(ic.ClientCxt, nameOrId, &options.DetachKeys, nil, bindings.PTrue, options.Stdin, options.Stdout, options.Stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) {
|
func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions, streams define.AttachStreams) (int, error) {
|
||||||
return 125, errors.New("not implemented")
|
return 125, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) {
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { //nolint
|
func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { //nolint
|
||||||
attachErr := make(chan error)
|
attachErr := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
Loading…
Reference in New Issue