diff options
Diffstat (limited to 'libpod/container_exec.go')
-rw-r--r-- | libpod/container_exec.go | 109 |
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) |