mirror of
https://github.com/containers/podman.git
synced 2025-06-23 18:59:30 +08:00
@ -138,7 +138,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
|
|||||||
)
|
)
|
||||||
createFlags.StringVar(
|
createFlags.StringVar(
|
||||||
&cf.DetachKeys,
|
&cf.DetachKeys,
|
||||||
"detach-keys", getDefaultDetachKeys(),
|
"detach-keys", GetDefaultDetachKeys(),
|
||||||
"Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`",
|
"Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`",
|
||||||
)
|
)
|
||||||
createFlags.StringSliceVar(
|
createFlags.StringSliceVar(
|
||||||
|
@ -116,6 +116,6 @@ func getDefaultPidsDescription() string {
|
|||||||
return "Tune container pids limit (set 0 for unlimited)"
|
return "Tune container pids limit (set 0 for unlimited)"
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultDetachKeys() string {
|
func GetDefaultDetachKeys() string {
|
||||||
return defaultContainerConfig.Engine.DetachKeys
|
return defaultContainerConfig.Engine.DetachKeys
|
||||||
}
|
}
|
||||||
|
60
cmd/podmanV2/containers/attach.go
Normal file
60
cmd/podmanV2/containers/attach.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/common"
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
attachDescription = "The podman attach command allows you to attach to a running container using the container's ID or name, either to view its ongoing output or to control it interactively."
|
||||||
|
attachCommand = &cobra.Command{
|
||||||
|
Use: "attach [flags] CONTAINER",
|
||||||
|
Short: "Attach to a running container",
|
||||||
|
Long: attachDescription,
|
||||||
|
RunE: attach,
|
||||||
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) > 1 || (len(args) == 0 && !cmd.Flag("latest").Changed) {
|
||||||
|
return errors.Errorf("attach requires the name or id of one running container or the latest flag")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
PreRunE: preRunE,
|
||||||
|
Example: `podman attach ctrID
|
||||||
|
podman attach 1234
|
||||||
|
podman attach --no-stdin foobar`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
attachOpts entities.AttachOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode},
|
||||||
|
Command: attachCommand,
|
||||||
|
})
|
||||||
|
flags := attachCommand.Flags()
|
||||||
|
flags.StringVar(&attachOpts.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`")
|
||||||
|
flags.BoolVar(&attachOpts.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false")
|
||||||
|
flags.BoolVar(&attachOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process")
|
||||||
|
flags.BoolVarP(&attachOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
|
||||||
|
if registry.IsRemote() {
|
||||||
|
_ = flags.MarkHidden("latest")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func attach(cmd *cobra.Command, args []string) error {
|
||||||
|
attachOpts.Stdin = os.Stdin
|
||||||
|
if attachOpts.NoStdin {
|
||||||
|
attachOpts.Stdin = nil
|
||||||
|
}
|
||||||
|
attachOpts.Stdout = os.Stdout
|
||||||
|
attachOpts.Stderr = os.Stderr
|
||||||
|
return registry.ContainerEngine().ContainerAttach(registry.GetContext(), args[0], attachOpts)
|
||||||
|
}
|
93
cmd/podmanV2/containers/exec.go
Normal file
93
cmd/podmanV2/containers/exec.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/common"
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
envLib "github.com/containers/libpod/pkg/env"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
execDescription = `Execute the specified command inside a running container.
|
||||||
|
`
|
||||||
|
execCommand = &cobra.Command{
|
||||||
|
Use: "exec [flags] CONTAINER [COMMAND [ARG...]]",
|
||||||
|
Short: "Run a process in a running container",
|
||||||
|
Long: execDescription,
|
||||||
|
PreRunE: preRunE,
|
||||||
|
RunE: exec,
|
||||||
|
Example: `podman exec -it ctrID ls
|
||||||
|
podman exec -it -w /tmp myCtr pwd
|
||||||
|
podman exec --user root ctrID ls`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
envInput, envFile []string
|
||||||
|
execOpts entities.ExecOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode},
|
||||||
|
Command: execCommand,
|
||||||
|
})
|
||||||
|
flags := execCommand.Flags()
|
||||||
|
flags.SetInterspersed(false)
|
||||||
|
flags.StringVar(&execOpts.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "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.StringSliceVar(&envFile, "env-file", []string{}, "Read in a file of environment variables")
|
||||||
|
flags.BoolVarP(&execOpts.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
|
||||||
|
flags.BoolVarP(&execOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
|
||||||
|
flags.BoolVar(&execOpts.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false")
|
||||||
|
flags.BoolVarP(&execOpts.Tty, "tty", "t", false, "Allocate a pseudo-TTY. The default is false")
|
||||||
|
flags.StringVarP(&execOpts.User, "user", "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command")
|
||||||
|
flags.UintVar(&execOpts.PreserveFDs, "preserve-fds", 0, "Pass N additional file descriptors to the container")
|
||||||
|
flags.StringVarP(&execOpts.WorkDir, "workdir", "w", "", "Working directory inside the container")
|
||||||
|
if registry.IsRemote() {
|
||||||
|
_ = flags.MarkHidden("latest")
|
||||||
|
_ = flags.MarkHidden("preserve-fds")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func exec(cmd *cobra.Command, args []string) error {
|
||||||
|
var nameOrId string
|
||||||
|
execOpts.Cmd = args
|
||||||
|
if !execOpts.Latest {
|
||||||
|
execOpts.Cmd = args[1:]
|
||||||
|
nameOrId = args[0]
|
||||||
|
}
|
||||||
|
// Validate given environment variables
|
||||||
|
execOpts.Envs = make(map[string]string)
|
||||||
|
for _, f := range envFile {
|
||||||
|
fileEnv, err := envLib.ParseFile(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
execOpts.Envs = envLib.Join(execOpts.Envs, fileEnv)
|
||||||
|
}
|
||||||
|
|
||||||
|
cliEnv, err := envLib.ParseSlice(envInput)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error parsing environment variables")
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
registry.SetExitCode(exitCode)
|
||||||
|
return err
|
||||||
|
}
|
@ -3,7 +3,6 @@ package libpod
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
@ -96,7 +95,7 @@ func (c *Container) Start(ctx context.Context, recursive bool) (err error) {
|
|||||||
// The channel will be closed automatically after the result of attach has been
|
// The channel will be closed automatically after the result of attach has been
|
||||||
// sent.
|
// sent.
|
||||||
// If recursive is set, StartAndAttach will also start all containers this container depends on.
|
// If recursive is set, StartAndAttach will also start all containers this container depends on.
|
||||||
func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) {
|
func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) {
|
||||||
if !c.batched {
|
if !c.batched {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
@ -213,29 +212,10 @@ func (c *Container) Kill(signal uint) error {
|
|||||||
return c.save()
|
return c.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AttachStreams contains streams that will be attached to the container
|
|
||||||
type AttachStreams struct {
|
|
||||||
// OutputStream will be attached to container's STDOUT
|
|
||||||
OutputStream io.WriteCloser
|
|
||||||
// ErrorStream will be attached to container's STDERR
|
|
||||||
ErrorStream io.WriteCloser
|
|
||||||
// InputStream will be attached to container's STDIN
|
|
||||||
InputStream *bufio.Reader
|
|
||||||
// AttachOutput is whether to attach to STDOUT
|
|
||||||
// If false, stdout will not be attached
|
|
||||||
AttachOutput bool
|
|
||||||
// AttachError is whether to attach to STDERR
|
|
||||||
// If false, stdout will not be attached
|
|
||||||
AttachError bool
|
|
||||||
// AttachInput is whether to attach to STDIN
|
|
||||||
// If false, stdout will not be attached
|
|
||||||
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
|
// 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.
|
// 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 {
|
func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize) error {
|
||||||
if !c.batched {
|
if !c.batched {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
if err := c.syncContainer(); err != nil {
|
if err := c.syncContainer(); err != nil {
|
||||||
|
@ -221,7 +221,7 @@ func (c *Container) ExecStart(sessionID string) error {
|
|||||||
// ExecStartAndAttach starts and attaches to an exec session in a container.
|
// ExecStartAndAttach starts and attaches to an exec session in a container.
|
||||||
// TODO: Should we include detach keys in the signature to allow override?
|
// TODO: Should we include detach keys in the signature to allow override?
|
||||||
// TODO: How do we handle AttachStdin/AttachStdout/AttachStderr?
|
// TODO: How do we handle AttachStdin/AttachStdout/AttachStderr?
|
||||||
func (c *Container) ExecStartAndAttach(sessionID string, streams *AttachStreams) error {
|
func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams) error {
|
||||||
if !c.batched {
|
if !c.batched {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
@ -544,7 +544,7 @@ func (c *Container) ExecResize(sessionID string, newSize remotecommand.TerminalS
|
|||||||
// Exec emulates the old Libpod exec API, providing a single call to create,
|
// Exec emulates the old Libpod exec API, providing a single call to create,
|
||||||
// run, and remove an exec session. Returns exit code and error. Exit code is
|
// run, and remove an exec session. Returns exit code and error. Exit code is
|
||||||
// not guaranteed to be set sanely if error is not nil.
|
// not guaranteed to be set sanely if error is not nil.
|
||||||
func (c *Container) Exec(config *ExecConfig, streams *AttachStreams, resize <-chan remotecommand.TerminalSize) (int, error) {
|
func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan remotecommand.TerminalSize) (int, error) {
|
||||||
sessionID, err := c.ExecCreate(config)
|
sessionID, err := c.ExecCreate(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
|
@ -112,7 +112,7 @@ func (c *Container) execPS(args []string) ([]string, error) {
|
|||||||
defer wErrPipe.Close()
|
defer wErrPipe.Close()
|
||||||
defer rErrPipe.Close()
|
defer rErrPipe.Close()
|
||||||
|
|
||||||
streams := new(AttachStreams)
|
streams := new(define.AttachStreams)
|
||||||
streams.OutputStream = wPipe
|
streams.OutputStream = wPipe
|
||||||
streams.ErrorStream = wErrPipe
|
streams.ErrorStream = wErrPipe
|
||||||
streams.AttachOutput = true
|
streams.AttachOutput = true
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
package define
|
package define
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultInfraImage to use for infra container
|
// DefaultInfraImage to use for infra container
|
||||||
DefaultInfraImage = "k8s.gcr.io/pause:3.2"
|
DefaultInfraImage = "k8s.gcr.io/pause:3.2"
|
||||||
@ -33,3 +38,22 @@ const (
|
|||||||
V2s2ManifestDir = "docker-dir"
|
V2s2ManifestDir = "docker-dir"
|
||||||
V2s2Archive = "docker-archive"
|
V2s2Archive = "docker-archive"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AttachStreams contains streams that will be attached to the container
|
||||||
|
type AttachStreams struct {
|
||||||
|
// OutputStream will be attached to container's STDOUT
|
||||||
|
OutputStream io.WriteCloser
|
||||||
|
// ErrorStream will be attached to container's STDERR
|
||||||
|
ErrorStream io.WriteCloser
|
||||||
|
// InputStream will be attached to container's STDIN
|
||||||
|
InputStream *bufio.Reader
|
||||||
|
// AttachOutput is whether to attach to STDOUT
|
||||||
|
// If false, stdout will not be attached
|
||||||
|
AttachOutput bool
|
||||||
|
// AttachError is whether to attach to STDERR
|
||||||
|
// If false, stdout will not be attached
|
||||||
|
AttachError bool
|
||||||
|
// AttachInput is whether to attach to STDIN
|
||||||
|
// If false, stdout will not be attached
|
||||||
|
AttachInput bool
|
||||||
|
}
|
||||||
|
@ -108,7 +108,7 @@ func (c *Container) runHealthCheck() (HealthCheckStatus, error) {
|
|||||||
hcw := hcWriteCloser{
|
hcw := hcWriteCloser{
|
||||||
captureBuffer,
|
captureBuffer,
|
||||||
}
|
}
|
||||||
streams := new(AttachStreams)
|
streams := new(define.AttachStreams)
|
||||||
streams.OutputStream = hcw
|
streams.OutputStream = hcw
|
||||||
streams.ErrorStream = hcw
|
streams.ErrorStream = hcw
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
"k8s.io/client-go/tools/remotecommand"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -141,7 +143,7 @@ type ExecOptions struct {
|
|||||||
// the container was run as will be used.
|
// the container was run as will be used.
|
||||||
User string
|
User string
|
||||||
// Streams are the streams that will be attached to the container.
|
// Streams are the streams that will be attached to the container.
|
||||||
Streams *AttachStreams
|
Streams *define.AttachStreams
|
||||||
// PreserveFDs is a number of additional file descriptors (in addition
|
// PreserveFDs is a number of additional file descriptors (in addition
|
||||||
// to 0, 1, 2) that will be passed to the executed process. The total FDs
|
// to 0, 1, 2) that will be passed to the executed process. The total FDs
|
||||||
// passed will be 3 + PreserveFDs.
|
// passed will be 3 + PreserveFDs.
|
||||||
|
@ -31,7 +31,7 @@ const (
|
|||||||
// Attach to the given container
|
// Attach to the given container
|
||||||
// Does not check if state is appropriate
|
// Does not check if state is appropriate
|
||||||
// started is only required if startContainer is true
|
// started is only required if startContainer is true
|
||||||
func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
|
func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
|
||||||
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
|
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
|
||||||
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
|
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan re
|
|||||||
// 4. attachToExec sends on startFd, signalling it has attached to the socket and child is ready to go
|
// 4. attachToExec sends on startFd, signalling it has attached to the socket and child is ready to go
|
||||||
// 5. child receives on startFd, runs the runtime exec command
|
// 5. child receives on startFd, runs the runtime exec command
|
||||||
// attachToExec is responsible for closing startFd and attachFd
|
// attachToExec is responsible for closing startFd and attachFd
|
||||||
func (c *Container) attachToExec(streams *AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File) error {
|
func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File) error {
|
||||||
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
|
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
|
||||||
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
|
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
|
||||||
}
|
}
|
||||||
@ -189,7 +189,7 @@ func buildSocketPath(socketPath string) string {
|
|||||||
return socketPath
|
return socketPath
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupStdioChannels(streams *AttachStreams, conn *net.UnixConn, detachKeys []byte) (chan error, chan error) {
|
func setupStdioChannels(streams *define.AttachStreams, conn *net.UnixConn, detachKeys []byte) (chan error, chan error) {
|
||||||
receiveStdoutError := make(chan error)
|
receiveStdoutError := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
receiveStdoutError <- redirectResponseToOutputStreams(streams.OutputStream, streams.ErrorStream, streams.AttachOutput, streams.AttachError, conn)
|
receiveStdoutError <- redirectResponseToOutputStreams(streams.OutputStream, streams.ErrorStream, streams.AttachOutput, streams.AttachError, conn)
|
||||||
@ -257,7 +257,7 @@ func redirectResponseToOutputStreams(outputStream, errorStream io.Writer, writeO
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func readStdio(streams *AttachStreams, receiveStdoutError, stdinDone chan error) error {
|
func readStdio(streams *define.AttachStreams, receiveStdoutError, stdinDone chan error) error {
|
||||||
var err error
|
var err error
|
||||||
select {
|
select {
|
||||||
case err = <-receiveStdoutError:
|
case err = <-receiveStdoutError:
|
||||||
|
@ -9,10 +9,10 @@ import (
|
|||||||
"k8s.io/client-go/tools/remotecommand"
|
"k8s.io/client-go/tools/remotecommand"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
|
func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
|
||||||
return define.ErrNotImplemented
|
return define.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) attachToExec(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, sessionID string, startFd *os.File, attachFd *os.File) error {
|
func (c *Container) attachToExec(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, sessionID string, startFd *os.File, attachFd *os.File) error {
|
||||||
return define.ErrNotImplemented
|
return define.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
@ -1004,7 +1004,7 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal
|
|||||||
}
|
}
|
||||||
env = envLib.Join(env, cliEnv)
|
env = envLib.Join(env, cliEnv)
|
||||||
|
|
||||||
streams := new(libpod.AttachStreams)
|
streams := new(define.AttachStreams)
|
||||||
streams.OutputStream = os.Stdout
|
streams.OutputStream = os.Stdout
|
||||||
streams.ErrorStream = os.Stderr
|
streams.ErrorStream = os.Stderr
|
||||||
if cli.Interactive {
|
if cli.Interactive {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
@ -14,7 +15,7 @@ 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 *libpod.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
|
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) {
|
||||||
resize := make(chan remotecommand.TerminalSize)
|
resize := make(chan remotecommand.TerminalSize)
|
||||||
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
|
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr,
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
streams := new(libpod.AttachStreams)
|
streams := new(define.AttachStreams)
|
||||||
streams.OutputStream = stdout
|
streams.OutputStream = stdout
|
||||||
streams.ErrorStream = stderr
|
streams.ErrorStream = stderr
|
||||||
streams.InputStream = bufio.NewReader(stdin)
|
streams.InputStream = bufio.NewReader(stdin)
|
||||||
|
@ -11,7 +11,7 @@ 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 *libpod.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
|
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) {
|
||||||
return -1, define.ErrNotImplemented
|
return -1, define.ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package entities
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/libpod/libpod/define"
|
"github.com/containers/libpod/libpod/define"
|
||||||
@ -157,3 +158,31 @@ type RestoreReport struct {
|
|||||||
type ContainerCreateReport struct {
|
type ContainerCreateReport struct {
|
||||||
Id string
|
Id string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AttachOptions describes the cli and other values
|
||||||
|
// needed to perform an attach
|
||||||
|
type AttachOptions struct {
|
||||||
|
DetachKeys string
|
||||||
|
Latest bool
|
||||||
|
NoStdin bool
|
||||||
|
SigProxy bool
|
||||||
|
Stdin *os.File
|
||||||
|
Stdout *os.File
|
||||||
|
Stderr *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecOptions describes the cli values to exec into
|
||||||
|
// a container
|
||||||
|
type ExecOptions struct {
|
||||||
|
Cmd []string
|
||||||
|
DetachKeys string
|
||||||
|
Envs map[string]string
|
||||||
|
Interactive bool
|
||||||
|
Latest bool
|
||||||
|
PreserveFDs uint
|
||||||
|
Privileged bool
|
||||||
|
Streams define.AttachStreams
|
||||||
|
Tty bool
|
||||||
|
User string
|
||||||
|
WorkDir string
|
||||||
|
}
|
||||||
|
@ -8,10 +8,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ContainerEngine interface {
|
type ContainerEngine interface {
|
||||||
|
ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error
|
||||||
ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error)
|
ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error)
|
||||||
ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error)
|
ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error)
|
||||||
ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error)
|
ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error)
|
||||||
ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
|
ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
|
||||||
|
ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error)
|
||||||
ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error)
|
ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error)
|
||||||
ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error)
|
ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error)
|
||||||
ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error
|
ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error
|
||||||
|
@ -5,6 +5,7 @@ package abi
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/buildah"
|
"github.com/containers/buildah"
|
||||||
@ -12,9 +13,9 @@ import (
|
|||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
"github.com/containers/libpod/libpod/define"
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/containers/libpod/libpod/image"
|
"github.com/containers/libpod/libpod/image"
|
||||||
"github.com/containers/libpod/pkg/adapter/shortcuts"
|
|
||||||
"github.com/containers/libpod/pkg/checkpoint"
|
"github.com/containers/libpod/pkg/checkpoint"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/domain/infra/abi/terminal"
|
||||||
"github.com/containers/libpod/pkg/signal"
|
"github.com/containers/libpod/pkg/signal"
|
||||||
"github.com/containers/libpod/pkg/specgen"
|
"github.com/containers/libpod/pkg/specgen"
|
||||||
"github.com/containers/libpod/pkg/specgen/generate"
|
"github.com/containers/libpod/pkg/specgen/generate"
|
||||||
@ -64,7 +65,7 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin
|
|||||||
var (
|
var (
|
||||||
responses []entities.WaitReport
|
responses []entities.WaitReport
|
||||||
)
|
)
|
||||||
ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
|
ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -90,7 +91,7 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri
|
|||||||
if options.All {
|
if options.All {
|
||||||
ctrs, err = ic.Libpod.GetAllContainers()
|
ctrs, err = ic.Libpod.GetAllContainers()
|
||||||
} else {
|
} else {
|
||||||
ctrs, err = shortcuts.GetContainersByContext(false, false, namesOrIds, ic.Libpod)
|
ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -111,7 +112,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st
|
|||||||
if options.All {
|
if options.All {
|
||||||
ctrs, err = ic.Libpod.GetAllContainers()
|
ctrs, err = ic.Libpod.GetAllContainers()
|
||||||
} else {
|
} else {
|
||||||
ctrs, err = shortcuts.GetContainersByContext(false, false, namesOrIds, ic.Libpod)
|
ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -135,7 +136,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin
|
|||||||
id := strings.Split(string(content), "\n")[0]
|
id := strings.Split(string(content), "\n")[0]
|
||||||
names = append(names, id)
|
names = append(names, id)
|
||||||
}
|
}
|
||||||
ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, names, ic.Libpod)
|
ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod)
|
||||||
if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
|
if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -171,7 +172,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
|
ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -187,7 +188,7 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st
|
|||||||
var (
|
var (
|
||||||
reports []*entities.RestartReport
|
reports []*entities.RestartReport
|
||||||
)
|
)
|
||||||
ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
|
ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -229,7 +230,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
|
|||||||
names = append(names, id)
|
names = append(names, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrs, err := shortcuts.GetContainersByContext(options.All, options.Latest, names, ic.Libpod)
|
ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod)
|
||||||
if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
|
if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
|
||||||
// Failed to get containers. If force is specified, get the containers ID
|
// Failed to get containers. If force is specified, get the containers ID
|
||||||
// and evict them
|
// and evict them
|
||||||
@ -277,7 +278,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
|
|||||||
|
|
||||||
func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) {
|
func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) {
|
||||||
var reports []*entities.ContainerInspectReport
|
var reports []*entities.ContainerInspectReport
|
||||||
ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
|
ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -455,3 +456,56 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG
|
|||||||
}
|
}
|
||||||
return &entities.ContainerCreateReport{Id: ctr.ID()}, nil
|
return &entities.ContainerCreateReport{Id: ctr.ID()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error {
|
||||||
|
ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctr := ctrs[0]
|
||||||
|
conState, err := ctr.State()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to determine state of %s", ctr.ID())
|
||||||
|
}
|
||||||
|
if conState != define.ContainerStateRunning {
|
||||||
|
return errors.Errorf("you can only attach to running containers")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the container is in a pod, also set to recursively start dependencies
|
||||||
|
if err := terminal.StartAttachCtr(ctx, ctr, options.Stdin, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != define.ErrDetach {
|
||||||
|
return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) {
|
||||||
|
ec := define.ExecErrorCodeGeneric
|
||||||
|
if options.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+int(options.PreserveFDs); i++ {
|
||||||
|
if _, found := m[i]; !found {
|
||||||
|
return ec, errors.New("invalid --preserve-fds=N specified. Not enough FDs available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod)
|
||||||
|
if err != nil {
|
||||||
|
return ec, err
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
return define.TranslateExecErrorToExitCode(ec, err), err
|
||||||
|
}
|
||||||
|
47
pkg/domain/infra/abi/terminal/sigproxy_linux.go
Normal file
47
pkg/domain/infra/abi/terminal/sigproxy_linux.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// +build ABISupport
|
||||||
|
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/pkg/signal"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProxySignals ...
|
||||||
|
func ProxySignals(ctr *libpod.Container) {
|
||||||
|
sigBuffer := make(chan os.Signal, 128)
|
||||||
|
signal.CatchAll(sigBuffer)
|
||||||
|
|
||||||
|
logrus.Debugf("Enabling signal proxying")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for s := range sigBuffer {
|
||||||
|
// Ignore SIGCHLD and SIGPIPE - these are mostly likely
|
||||||
|
// intended for the podman command itself.
|
||||||
|
// SIGURG was added because of golang 1.14 and its preemptive changes
|
||||||
|
// causing more signals to "show up".
|
||||||
|
// https://github.com/containers/libpod/issues/5483
|
||||||
|
if s == syscall.SIGCHLD || s == syscall.SIGPIPE || s == syscall.SIGURG {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctr.Kill(uint(s.(syscall.Signal))); err != nil {
|
||||||
|
// If the container dies, and we find out here,
|
||||||
|
// we need to forward that one signal to
|
||||||
|
// ourselves so that it is not lost, and then
|
||||||
|
// we terminate the proxy and let the defaults
|
||||||
|
// play out.
|
||||||
|
logrus.Errorf("Error forwarding signal %d to container %s: %v", s, ctr.ID(), err)
|
||||||
|
signal.StopCatch(sigBuffer)
|
||||||
|
if err := syscall.Kill(syscall.Getpid(), s.(syscall.Signal)); err != nil {
|
||||||
|
logrus.Errorf("failed to kill pid %d", syscall.Getpid())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
103
pkg/domain/infra/abi/terminal/terminal.go
Normal file
103
pkg/domain/infra/abi/terminal/terminal.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// +build ABISupport
|
||||||
|
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
|
lsignal "github.com/containers/libpod/pkg/signal"
|
||||||
|
"github.com/docker/docker/pkg/term"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"k8s.io/client-go/tools/remotecommand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RawTtyFormatter ...
|
||||||
|
type RawTtyFormatter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// getResize returns a TerminalSize command matching stdin's current
|
||||||
|
// size on success, and nil on errors.
|
||||||
|
func getResize() *remotecommand.TerminalSize {
|
||||||
|
winsize, err := term.GetWinsize(os.Stdin.Fd())
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warnf("Could not get terminal size %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &remotecommand.TerminalSize{
|
||||||
|
Width: winsize.Width,
|
||||||
|
Height: winsize.Height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper for prepareAttach - set up a goroutine to generate terminal resize events
|
||||||
|
func resizeTty(ctx context.Context, resize chan remotecommand.TerminalSize) {
|
||||||
|
sigchan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigchan, lsignal.SIGWINCH)
|
||||||
|
go func() {
|
||||||
|
defer close(resize)
|
||||||
|
// Update the terminal size immediately without waiting
|
||||||
|
// for a SIGWINCH to get the correct initial size.
|
||||||
|
resizeEvent := getResize()
|
||||||
|
for {
|
||||||
|
if resizeEvent == nil {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-sigchan:
|
||||||
|
resizeEvent = getResize()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-sigchan:
|
||||||
|
resizeEvent = getResize()
|
||||||
|
case resize <- *resizeEvent:
|
||||||
|
resizeEvent = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreTerminal(state *term.State) error {
|
||||||
|
logrus.SetFormatter(&logrus.TextFormatter{})
|
||||||
|
return term.RestoreTerminal(os.Stdin.Fd(), state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format ...
|
||||||
|
func (f *RawTtyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||||
|
textFormatter := logrus.TextFormatter{}
|
||||||
|
bytes, err := textFormatter.Format(entry)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
bytes = append(bytes, '\r')
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
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 cancel, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cancel, oldTermState, nil
|
||||||
|
}
|
123
pkg/domain/infra/abi/terminal/terminal_linux.go
Normal file
123
pkg/domain/infra/abi/terminal/terminal_linux.go
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// +build ABISupport
|
||||||
|
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
"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 map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, 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 func() {
|
||||||
|
if err := restoreTerminal(oldTermState); err != nil {
|
||||||
|
logrus.Errorf("unable to restore terminal: %q", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool, recursive bool) error { //nolint-interfacer
|
||||||
|
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 && ctr.Spec().Process.Terminal {
|
||||||
|
cancel, oldTermState, err := handleTerminalAttach(ctx, resize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := restoreTerminal(oldTermState); err != nil {
|
||||||
|
logrus.Errorf("unable to restore terminal: %q", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
streams := new(define.AttachStreams)
|
||||||
|
streams.OutputStream = stdout
|
||||||
|
streams.ErrorStream = stderr
|
||||||
|
streams.InputStream = bufio.NewReader(stdin)
|
||||||
|
streams.AttachOutput = true
|
||||||
|
streams.AttachError = true
|
||||||
|
streams.AttachInput = true
|
||||||
|
|
||||||
|
if stdout == nil {
|
||||||
|
logrus.Debugf("Not attaching to stdout")
|
||||||
|
streams.AttachOutput = false
|
||||||
|
}
|
||||||
|
if stderr == nil {
|
||||||
|
logrus.Debugf("Not attaching to stderr")
|
||||||
|
streams.AttachError = false
|
||||||
|
}
|
||||||
|
if stdin == nil {
|
||||||
|
logrus.Debugf("Not attaching to stdin")
|
||||||
|
streams.AttachInput = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !startContainer {
|
||||||
|
if sigProxy {
|
||||||
|
ProxySignals(ctr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctr.Attach(streams, detachKeys, resize)
|
||||||
|
}
|
||||||
|
|
||||||
|
attachChan, err := ctr.StartAndAttach(ctx, streams, detachKeys, resize, recursive)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sigProxy {
|
||||||
|
ProxySignals(ctr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stdout == nil && stderr == nil {
|
||||||
|
fmt.Printf("%s\n", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = <-attachChan
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -305,3 +305,11 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG
|
|||||||
}
|
}
|
||||||
return &entities.ContainerCreateReport{Id: response.ID}, nil
|
return &entities.ContainerCreateReport{Id: response.ID}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error {
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) {
|
||||||
|
return 125, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
"k8s.io/client-go/tools/remotecommand"
|
"k8s.io/client-go/tools/remotecommand"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *libpod.AttachStreams) {
|
func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *define.AttachStreams) {
|
||||||
|
|
||||||
// These are the varlink sockets
|
// These are the varlink sockets
|
||||||
reader := call.Call.Reader
|
reader := call.Call.Reader
|
||||||
@ -30,7 +30,7 @@ func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.
|
|||||||
// TODO if runc ever starts passing stderr, we can too
|
// TODO if runc ever starts passing stderr, we can too
|
||||||
// stderrWriter := NewVirtWriteCloser(writer, ToStderr)
|
// stderrWriter := NewVirtWriteCloser(writer, ToStderr)
|
||||||
|
|
||||||
streams := libpod.AttachStreams{
|
streams := define.AttachStreams{
|
||||||
OutputStream: stdoutWriter,
|
OutputStream: stdoutWriter,
|
||||||
InputStream: bufio.NewReader(pr),
|
InputStream: bufio.NewReader(pr),
|
||||||
// Runc eats the error stream
|
// Runc eats the error stream
|
||||||
@ -117,7 +117,7 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st
|
|||||||
return call.Writer.Flush()
|
return call.Writer.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
func attach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
|
func attach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
|
||||||
go func() {
|
go func() {
|
||||||
if err := ctr.Attach(streams, detachKeys, resize); err != nil {
|
if err := ctr.Attach(streams, detachKeys, resize); err != nil {
|
||||||
errChan <- err
|
errChan <- err
|
||||||
@ -127,7 +127,7 @@ func attach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys str
|
|||||||
return attachError
|
return attachError
|
||||||
}
|
}
|
||||||
|
|
||||||
func startAndAttach(ctr *libpod.Container, streams *libpod.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
|
func startAndAttach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
|
||||||
var finalErr error
|
var finalErr error
|
||||||
attachChan, err := ctr.StartAndAttach(getContext(), streams, detachKeys, resize, false)
|
attachChan, err := ctr.StartAndAttach(getContext(), streams, detachKeys, resize, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Reference in New Issue
Block a user