summaryrefslogtreecommitdiff
path: root/libpod/container_exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/container_exec.go')
-rw-r--r--libpod/container_exec.go109
1 files changed, 62 insertions, 47 deletions
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index be00c6fbe..d3c80e896 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -2,6 +2,8 @@ package libpod
import (
"context"
+ "errors"
+ "fmt"
"io/ioutil"
"net/http"
"os"
@@ -9,10 +11,10 @@ import (
"strconv"
"time"
+ "github.com/containers/common/pkg/resize"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/storage/pkg/stringid"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@@ -112,7 +114,7 @@ func (e *ExecSession) ContainerID() string {
// configuration and current state.
func (e *ExecSession) Inspect() (*define.InspectExecSession, error) {
if e.Config == nil {
- return nil, errors.Wrapf(define.ErrInternal, "given exec session does not have a configuration block")
+ return nil, fmt.Errorf("given exec session does not have a configuration block: %w", define.ErrInternal)
}
output := new(define.InspectExecSession)
@@ -165,18 +167,18 @@ func (c *Container) ExecCreate(config *ExecConfig) (string, error) {
// Verify our config
if config == nil {
- return "", errors.Wrapf(define.ErrInvalidArg, "must provide a configuration to ExecCreate")
+ return "", fmt.Errorf("must provide a configuration to ExecCreate: %w", define.ErrInvalidArg)
}
if len(config.Command) == 0 {
- return "", errors.Wrapf(define.ErrInvalidArg, "must provide a non-empty command to start an exec session")
+ return "", fmt.Errorf("must provide a non-empty command to start an exec session: %w", define.ErrInvalidArg)
}
if config.ExitCommandDelay > 0 && len(config.ExitCommand) == 0 {
- return "", errors.Wrapf(define.ErrInvalidArg, "must provide a non-empty exit command if giving an exit command delay")
+ return "", fmt.Errorf("must provide a non-empty exit command if giving an exit command delay: %w", define.ErrInvalidArg)
}
// Verify that we are in a good state to continue
if !c.ensureState(define.ContainerStateRunning) {
- return "", errors.Wrapf(define.ErrCtrStateInvalid, "can only create exec sessions on running containers")
+ return "", fmt.Errorf("can only create exec sessions on running containers: %w", define.ErrCtrStateInvalid)
}
// Generate an ID for our new exec session
@@ -203,7 +205,7 @@ func (c *Container) ExecCreate(config *ExecConfig) (string, error) {
session.State = define.ExecStateCreated
session.Config = new(ExecConfig)
if err := JSONDeepCopy(config, session.Config); err != nil {
- return "", errors.Wrapf(err, "error copying exec configuration into exec session")
+ return "", fmt.Errorf("error copying exec configuration into exec session: %w", err)
}
if len(session.Config.ExitCommand) > 0 {
@@ -243,16 +245,16 @@ func (c *Container) ExecStart(sessionID string) error {
// Verify that we are in a good state to continue
if !c.ensureState(define.ContainerStateRunning) {
- return errors.Wrapf(define.ErrCtrStateInvalid, "can only start exec sessions when their container is running")
+ return fmt.Errorf("can only start exec sessions when their container is running: %w", define.ErrCtrStateInvalid)
}
session, ok := c.state.ExecSessions[sessionID]
if !ok {
- return errors.Wrapf(define.ErrNoSuchExecSession, "container %s has no exec session with ID %s", c.ID(), sessionID)
+ return fmt.Errorf("container %s has no exec session with ID %s: %w", c.ID(), sessionID, define.ErrNoSuchExecSession)
}
if session.State != define.ExecStateCreated {
- return errors.Wrapf(define.ErrExecSessionStateInvalid, "can only start created exec sessions, while container %s session %s state is %q", c.ID(), session.ID(), session.State.String())
+ return fmt.Errorf("can only start created exec sessions, while container %s session %s state is %q: %w", c.ID(), session.ID(), session.State.String(), define.ErrExecSessionStateInvalid)
}
logrus.Infof("Going to start container %s exec session %s and attach to it", c.ID(), session.ID())
@@ -277,9 +279,13 @@ func (c *Container) ExecStart(sessionID string) error {
return c.save()
}
+func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams, newSize *resize.TerminalSize) error {
+ return c.execStartAndAttach(sessionID, streams, newSize, false)
+}
+
// ExecStartAndAttach starts and attaches to an exec session in a container.
// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty
-func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams, newSize *define.TerminalSize) error {
+func (c *Container) execStartAndAttach(sessionID string, streams *define.AttachStreams, newSize *resize.TerminalSize, isHealthcheck bool) error {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -291,16 +297,16 @@ func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachS
// Verify that we are in a good state to continue
if !c.ensureState(define.ContainerStateRunning) {
- return errors.Wrapf(define.ErrCtrStateInvalid, "can only start exec sessions when their container is running")
+ return fmt.Errorf("can only start exec sessions when their container is running: %w", define.ErrCtrStateInvalid)
}
session, ok := c.state.ExecSessions[sessionID]
if !ok {
- return errors.Wrapf(define.ErrNoSuchExecSession, "container %s has no exec session with ID %s", c.ID(), sessionID)
+ return fmt.Errorf("container %s has no exec session with ID %s: %w", c.ID(), sessionID, define.ErrNoSuchExecSession)
}
if session.State != define.ExecStateCreated {
- return errors.Wrapf(define.ErrExecSessionStateInvalid, "can only start created exec sessions, while container %s session %s state is %q", c.ID(), session.ID(), session.State.String())
+ return fmt.Errorf("can only start created exec sessions, while container %s session %s state is %q: %w", c.ID(), session.ID(), session.State.String(), define.ErrExecSessionStateInvalid)
}
logrus.Infof("Going to start container %s exec session %s and attach to it", c.ID(), session.ID())
@@ -315,7 +321,12 @@ func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachS
return err
}
- c.newContainerEvent(events.Exec)
+ if isHealthcheck {
+ c.newContainerEvent(events.HealthStatus)
+ } else {
+ c.newContainerEvent(events.Exec)
+ }
+
logrus.Debugf("Successfully started exec session %s in container %s", session.ID(), c.ID())
var lastErr error
@@ -361,7 +372,7 @@ func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachS
if lastErr != nil {
logrus.Errorf("Container %s exec session %s error: %v", c.ID(), session.ID(), lastErr)
}
- return errors.Wrapf(err, "error syncing container %s state to update exec session %s", c.ID(), sessionID)
+ return fmt.Errorf("error syncing container %s state to update exec session %s: %w", c.ID(), sessionID, err)
}
// Now handle the error from readExecExitCode above.
@@ -413,7 +424,7 @@ func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachS
// ExecHTTPStartAndAttach starts and performs an HTTP attach to an exec session.
// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty
func (c *Container) ExecHTTPStartAndAttach(sessionID string, r *http.Request, w http.ResponseWriter,
- streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool, newSize *define.TerminalSize) error {
+ streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool, newSize *resize.TerminalSize) error {
// TODO: How do we combine streams with the default streams set in the exec session?
// Ensure that we don't leak a goroutine here
@@ -432,16 +443,16 @@ func (c *Container) ExecHTTPStartAndAttach(sessionID string, r *http.Request, w
session, ok := c.state.ExecSessions[sessionID]
if !ok {
- return errors.Wrapf(define.ErrNoSuchExecSession, "container %s has no exec session with ID %s", c.ID(), sessionID)
+ return fmt.Errorf("container %s has no exec session with ID %s: %w", c.ID(), sessionID, define.ErrNoSuchExecSession)
}
// Verify that we are in a good state to continue
if !c.ensureState(define.ContainerStateRunning) {
- return errors.Wrapf(define.ErrCtrStateInvalid, "can only start exec sessions when their container is running")
+ return fmt.Errorf("can only start exec sessions when their container is running: %w", define.ErrCtrStateInvalid)
}
if session.State != define.ExecStateCreated {
- return errors.Wrapf(define.ErrExecSessionStateInvalid, "can only start created exec sessions, while container %s session %s state is %q", c.ID(), session.ID(), session.State.String())
+ return fmt.Errorf("can only start created exec sessions, while container %s session %s state is %q: %w", c.ID(), session.ID(), session.State.String(), define.ErrExecSessionStateInvalid)
}
logrus.Infof("Going to start container %s exec session %s and attach to it", c.ID(), session.ID())
@@ -558,11 +569,11 @@ func (c *Container) ExecStop(sessionID string, timeout *uint) error {
session, ok := c.state.ExecSessions[sessionID]
if !ok {
- return errors.Wrapf(define.ErrNoSuchExecSession, "container %s has no exec session with ID %s", c.ID(), sessionID)
+ return fmt.Errorf("container %s has no exec session with ID %s: %w", c.ID(), sessionID, define.ErrNoSuchExecSession)
}
if session.State != define.ExecStateRunning {
- return errors.Wrapf(define.ErrExecSessionStateInvalid, "container %s exec session %s is %q, can only stop running sessions", c.ID(), session.ID(), session.State.String())
+ return fmt.Errorf("container %s exec session %s is %q, can only stop running sessions: %w", c.ID(), session.ID(), session.State.String(), define.ErrExecSessionStateInvalid)
}
logrus.Infof("Stopping container %s exec session %s", c.ID(), session.ID())
@@ -608,7 +619,7 @@ func (c *Container) ExecCleanup(sessionID string) error {
session, ok := c.state.ExecSessions[sessionID]
if !ok {
- return errors.Wrapf(define.ErrNoSuchExecSession, "container %s has no exec session with ID %s", c.ID(), sessionID)
+ return fmt.Errorf("container %s has no exec session with ID %s: %w", c.ID(), sessionID, define.ErrNoSuchExecSession)
}
if session.State == define.ExecStateRunning {
@@ -619,7 +630,7 @@ func (c *Container) ExecCleanup(sessionID string) error {
}
if alive {
- return errors.Wrapf(define.ErrExecSessionStateInvalid, "cannot clean up container %s exec session %s as it is running", c.ID(), session.ID())
+ return fmt.Errorf("cannot clean up container %s exec session %s as it is running: %w", c.ID(), session.ID(), define.ErrExecSessionStateInvalid)
}
if err := retrieveAndWriteExecExitCode(c, session.ID()); err != nil {
@@ -646,7 +657,7 @@ func (c *Container) ExecRemove(sessionID string, force bool) error {
session, ok := c.state.ExecSessions[sessionID]
if !ok {
- return errors.Wrapf(define.ErrNoSuchExecSession, "container %s has no exec session with ID %s", c.ID(), sessionID)
+ return fmt.Errorf("container %s has no exec session with ID %s: %w", c.ID(), sessionID, define.ErrNoSuchExecSession)
}
logrus.Infof("Removing container %s exec session %s", c.ID(), session.ID())
@@ -667,7 +678,7 @@ func (c *Container) ExecRemove(sessionID string, force bool) error {
if session.State == define.ExecStateRunning {
if !force {
- return errors.Wrapf(define.ErrExecSessionStateInvalid, "container %s exec session %s is still running, cannot remove", c.ID(), session.ID())
+ return fmt.Errorf("container %s exec session %s is still running, cannot remove: %w", c.ID(), session.ID(), define.ErrExecSessionStateInvalid)
}
// Stop the session
@@ -701,7 +712,7 @@ func (c *Container) ExecRemove(sessionID string, force bool) error {
// ExecResize resizes the TTY of the given exec session. Only available if the
// exec session created a TTY.
-func (c *Container) ExecResize(sessionID string, newSize define.TerminalSize) error {
+func (c *Container) ExecResize(sessionID string, newSize resize.TerminalSize) error {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -713,13 +724,13 @@ func (c *Container) ExecResize(sessionID string, newSize define.TerminalSize) er
session, ok := c.state.ExecSessions[sessionID]
if !ok {
- return errors.Wrapf(define.ErrNoSuchExecSession, "container %s has no exec session with ID %s", c.ID(), sessionID)
+ return fmt.Errorf("container %s has no exec session with ID %s: %w", c.ID(), sessionID, define.ErrNoSuchExecSession)
}
logrus.Infof("Resizing container %s exec session %s to %+v", c.ID(), session.ID(), newSize)
if session.State != define.ExecStateRunning {
- return errors.Wrapf(define.ErrExecSessionStateInvalid, "cannot resize container %s exec session %s as it is not running", c.ID(), session.ID())
+ return fmt.Errorf("cannot resize container %s exec session %s as it is not running: %w", c.ID(), session.ID(), define.ErrExecSessionStateInvalid)
}
// The exec session may have exited since we last updated.
@@ -735,7 +746,7 @@ func (c *Container) ExecResize(sessionID string, newSize define.TerminalSize) er
logrus.Errorf("Saving state of container %s: %v", c.ID(), err)
}
- return errors.Wrapf(define.ErrExecSessionStateInvalid, "cannot resize container %s exec session %s as it has stopped", c.ID(), session.ID())
+ return fmt.Errorf("cannot resize container %s exec session %s as it has stopped: %w", c.ID(), session.ID(), define.ErrExecSessionStateInvalid)
}
// Make sure the exec session is still running.
@@ -743,10 +754,14 @@ func (c *Container) ExecResize(sessionID string, newSize define.TerminalSize) er
return c.ociRuntime.ExecAttachResize(c, sessionID, newSize)
}
+func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan resize.TerminalSize) (int, error) {
+ return c.exec(config, streams, resize, false)
+}
+
// 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
// not guaranteed to be set sanely if error is not nil.
-func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan define.TerminalSize) (int, error) {
+func (c *Container) exec(config *ExecConfig, streams *define.AttachStreams, resizeChan <-chan resize.TerminalSize, isHealthcheck bool) (int, error) {
sessionID, err := c.ExecCreate(config)
if err != nil {
return -1, err
@@ -759,15 +774,15 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
// API there.
// TODO: Refactor so this is closed here, before we remove the exec
// session.
- var size *define.TerminalSize
- if resize != nil {
- s := <-resize
+ var size *resize.TerminalSize
+ if resizeChan != nil {
+ s := <-resizeChan
size = &s
go func() {
logrus.Debugf("Sending resize events to exec session %s", sessionID)
- for resizeRequest := range resize {
+ for resizeRequest := range resizeChan {
if err := c.ExecResize(sessionID, resizeRequest); err != nil {
- if errors.Cause(err) == define.ErrExecSessionStateInvalid {
+ if errors.Is(err, define.ErrExecSessionStateInvalid) {
// The exec session stopped
// before we could resize.
logrus.Infof("Missed resize on exec session %s, already stopped", sessionID)
@@ -780,13 +795,13 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
}()
}
- if err := c.ExecStartAndAttach(sessionID, streams, size); err != nil {
+ if err := c.execStartAndAttach(sessionID, streams, size, isHealthcheck); err != nil {
return -1, err
}
session, err := c.execSessionNoCopy(sessionID)
if err != nil {
- if errors.Cause(err) == define.ErrNoSuchExecSession {
+ if errors.Is(err, define.ErrNoSuchExecSession) {
// TODO: If a proper Context is ever plumbed in here, we
// should use it.
// As things stand, though, it's not worth it - this
@@ -794,7 +809,7 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
// streaming.
diedEvent, err := c.runtime.GetExecDiedEvent(context.Background(), c.ID(), sessionID)
if err != nil {
- return -1, errors.Wrapf(err, "error retrieving exec session %s exit code", sessionID)
+ return -1, fmt.Errorf("error retrieving exec session %s exit code: %w", sessionID, err)
}
return diedEvent.ContainerExitCode, nil
}
@@ -802,7 +817,7 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
}
exitCode := session.ExitCode
if err := c.ExecRemove(sessionID, false); err != nil {
- if errors.Cause(err) == define.ErrNoSuchExecSession {
+ if errors.Is(err, define.ErrNoSuchExecSession) {
return exitCode, nil
}
return -1, err
@@ -824,7 +839,7 @@ func (c *Container) cleanupExecBundle(sessionID string) (err error) {
}
if pathErr, ok := err.(*os.PathError); ok {
err = pathErr.Err
- if errors.Cause(err) == unix.ENOTEMPTY || errors.Cause(err) == unix.EBUSY {
+ if errors.Is(err, unix.ENOTEMPTY) || errors.Is(err, unix.EBUSY) {
// give other processes a chance to use the container
if !c.batched {
if err := c.save(); err != nil {
@@ -896,7 +911,7 @@ func (c *Container) createExecBundle(sessionID string) (retErr error) {
if err := os.MkdirAll(c.execExitFileDir(sessionID), execDirPermission); err != nil {
// The directory is allowed to exist
if !os.IsExist(err) {
- return errors.Wrapf(err, "error creating OCI runtime exit file path %s", c.execExitFileDir(sessionID))
+ return fmt.Errorf("error creating OCI runtime exit file path %s: %w", c.execExitFileDir(sessionID), err)
}
}
return nil
@@ -935,7 +950,7 @@ func (c *Container) getExecSessionPID(sessionID string) (int, error) {
return oldSession.PID, nil
}
- return -1, errors.Wrapf(define.ErrNoSuchExecSession, "no exec session with ID %s found in container %s", sessionID, c.ID())
+ return -1, fmt.Errorf("no exec session with ID %s found in container %s: %w", sessionID, c.ID(), define.ErrNoSuchExecSession)
}
// getKnownExecSessions gets a list of all exec sessions we think are running,
@@ -1049,7 +1064,7 @@ func (c *Container) removeAllExecSessions() error {
}
// Delete all exec sessions
if err := c.runtime.state.RemoveContainerExecSessions(c); err != nil {
- if errors.Cause(err) != define.ErrCtrRemoved {
+ if !errors.Is(err, define.ErrCtrRemoved) {
if lastErr != nil {
logrus.Errorf("Stopping container %s exec sessions: %v", c.ID(), lastErr)
}
@@ -1059,7 +1074,7 @@ func (c *Container) removeAllExecSessions() error {
c.state.ExecSessions = nil
c.state.LegacyExecSessions = nil
if err := c.save(); err != nil {
- if errors.Cause(err) != define.ErrCtrRemoved {
+ if !errors.Is(err, define.ErrCtrRemoved) {
if lastErr != nil {
logrus.Errorf("Stopping container %s exec sessions: %v", c.ID(), lastErr)
}
@@ -1100,13 +1115,13 @@ func writeExecExitCode(c *Container, sessionID string, exitCode int) error {
// If we can't do this, no point in continuing, any attempt to save
// would write garbage to the DB.
if err := c.syncContainer(); err != nil {
- if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrRemoved {
+ if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) {
// Container's entirely removed. We can't save status,
// but the container's entirely removed, so we don't
// need to. Exit without error.
return nil
}
- return errors.Wrapf(err, "error syncing container %s state to remove exec session %s", c.ID(), sessionID)
+ return fmt.Errorf("error syncing container %s state to remove exec session %s: %w", c.ID(), sessionID, err)
}
return justWriteExecExitCode(c, sessionID, exitCode)