mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #43563 from thaJeztah/less_signal_conversions
pass syscall.Signal for stop-signals to reduce type conversions
This commit is contained in:
commit
b3675e1839
22 changed files with 90 additions and 107 deletions
|
@ -33,7 +33,7 @@ type copyBackend interface {
|
||||||
// stateBackend includes functions to implement to provide container state lifecycle functionality.
|
// stateBackend includes functions to implement to provide container state lifecycle functionality.
|
||||||
type stateBackend interface {
|
type stateBackend interface {
|
||||||
ContainerCreate(config types.ContainerCreateConfig) (container.CreateResponse, error)
|
ContainerCreate(config types.ContainerCreateConfig) (container.CreateResponse, error)
|
||||||
ContainerKill(name string, sig uint64) error
|
ContainerKill(name string, signal string) error
|
||||||
ContainerPause(name string) error
|
ContainerPause(name string) error
|
||||||
ContainerRename(oldName, newName string) error
|
ContainerRename(oldName, newName string) error
|
||||||
ContainerResize(name string, height, width int) error
|
ContainerResize(name string, height, width int) error
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
"github.com/containerd/containerd/platforms"
|
||||||
"github.com/docker/docker/api/server/httpstatus"
|
"github.com/docker/docker/api/server/httpstatus"
|
||||||
|
@ -254,18 +253,8 @@ func (s *containerRouter) postContainersKill(ctx context.Context, w http.Respons
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var sig syscall.Signal
|
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
|
if err := s.backend.ContainerKill(name, r.Form.Get("signal")); err != nil {
|
||||||
// If we have a signal, look at it. Otherwise, do nothing
|
|
||||||
if sigStr := r.Form.Get("signal"); sigStr != "" {
|
|
||||||
var err error
|
|
||||||
if sig, err = signal.ParseSignal(sigStr); err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
|
|
||||||
var isStopped bool
|
var isStopped bool
|
||||||
if errdefs.IsConflict(err) {
|
if errdefs.IsConflict(err) {
|
||||||
isStopped = true
|
isStopped = true
|
||||||
|
|
|
@ -65,7 +65,7 @@ type ExecBackend interface {
|
||||||
// ContainerRm removes a container specified by `id`.
|
// ContainerRm removes a container specified by `id`.
|
||||||
ContainerRm(name string, config *types.ContainerRmConfig) error
|
ContainerRm(name string, config *types.ContainerRmConfig) error
|
||||||
// ContainerKill stops the container execution abruptly.
|
// ContainerKill stops the container execution abruptly.
|
||||||
ContainerKill(containerID string, sig uint64) error
|
ContainerKill(containerID string, sig string) error
|
||||||
// ContainerStart starts a new container
|
// ContainerStart starts a new container
|
||||||
ContainerStart(containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
|
ContainerStart(containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
|
||||||
// ContainerWait stops processing until the given container is stopped.
|
// ContainerWait stops processing until the given container is stopped.
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (c *containerManager) Run(ctx context.Context, cID string, stdout, stderr i
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
logrus.Debugln("Build cancelled, killing and removing container:", cID)
|
logrus.Debugln("Build cancelled, killing and removing container:", cID)
|
||||||
c.backend.ContainerKill(cID, 0)
|
c.backend.ContainerKill(cID, "")
|
||||||
c.removeContainer(cID, stdout)
|
c.removeContainer(cID, stdout)
|
||||||
cancelErrCh <- errCancelled
|
cancelErrCh <- errCancelled
|
||||||
case <-finished:
|
case <-finished:
|
||||||
|
|
|
@ -46,7 +46,7 @@ func (m *MockBackend) CommitBuildStep(c backend.CommitConfig) (image.ID, error)
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockBackend) ContainerKill(containerID string, sig uint64) error {
|
func (m *MockBackend) ContainerKill(containerID string, sig string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ func (l *mockLayer) NewRWLayer() (builder.RWLayer, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *mockLayer) DiffID() layer.DiffID {
|
func (l *mockLayer) DiffID() layer.DiffID {
|
||||||
return layer.DiffID("abcdef")
|
return "abcdef"
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockRWLayer struct {
|
type mockRWLayer struct {
|
||||||
|
|
|
@ -511,16 +511,16 @@ func (container *Container) IsDestinationMounted(destination string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopSignal returns the signal used to stop the container.
|
// StopSignal returns the signal used to stop the container.
|
||||||
func (container *Container) StopSignal() int {
|
func (container *Container) StopSignal() syscall.Signal {
|
||||||
var stopSignal syscall.Signal
|
var stopSignal syscall.Signal
|
||||||
if container.Config.StopSignal != "" {
|
if container.Config.StopSignal != "" {
|
||||||
stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
|
stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(stopSignal) == 0 {
|
if stopSignal == 0 {
|
||||||
stopSignal, _ = signal.ParseSignal(defaultStopSignal)
|
stopSignal, _ = signal.ParseSignal(defaultStopSignal)
|
||||||
}
|
}
|
||||||
return int(stopSignal)
|
return stopSignal
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopTimeout returns the timeout (in seconds) used to stop the container.
|
// StopTimeout returns the timeout (in seconds) used to stop the container.
|
||||||
|
|
|
@ -24,7 +24,7 @@ func TestContainerStopSignal(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
s := c.StopSignal()
|
s := c.StopSignal()
|
||||||
if s != int(def) {
|
if s != def {
|
||||||
t.Fatalf("Expected %v, got %v", def, s)
|
t.Fatalf("Expected %v, got %v", def, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ type Backend interface {
|
||||||
ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error)
|
ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error)
|
||||||
ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
|
ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
|
||||||
ContainerRm(name string, config *types.ContainerRmConfig) error
|
ContainerRm(name string, config *types.ContainerRmConfig) error
|
||||||
ContainerKill(name string, sig uint64) error
|
ContainerKill(name string, sig string) error
|
||||||
SetContainerDependencyStore(name string, store exec.DependencyGetter) error
|
SetContainerDependencyStore(name string, store exec.DependencyGetter) error
|
||||||
SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error
|
SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error
|
||||||
SetContainerConfigReferences(name string, refs []*swarmtypes.ConfigReference) error
|
SetContainerConfigReferences(name string, refs []*swarmtypes.ConfigReference) error
|
||||||
|
|
|
@ -417,7 +417,7 @@ func (c *containerAdapter) shutdown(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerAdapter) terminate(ctx context.Context) error {
|
func (c *containerAdapter) terminate(ctx context.Context) error {
|
||||||
return c.backend.ContainerKill(c.container.name(), uint64(syscall.SIGKILL))
|
return c.backend.ContainerKill(c.container.name(), syscall.SIGKILL.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerAdapter) remove(ctx context.Context) error {
|
func (c *containerAdapter) remove(ctx context.Context) error {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/links"
|
"github.com/docker/docker/daemon/links"
|
||||||
|
@ -336,24 +337,25 @@ func (daemon *Daemon) cleanupSecretDir(c *container.Container) {
|
||||||
|
|
||||||
func killProcessDirectly(container *container.Container) error {
|
func killProcessDirectly(container *container.Container) error {
|
||||||
pid := container.GetPID()
|
pid := container.GetPID()
|
||||||
// Ensure that we don't kill ourselves
|
|
||||||
if pid == 0 {
|
if pid == 0 {
|
||||||
|
// Ensure that we don't kill ourselves
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := unix.Kill(pid, 9); err != nil {
|
if err := unix.Kill(pid, syscall.SIGKILL); err != nil {
|
||||||
if err != unix.ESRCH {
|
if err != unix.ESRCH {
|
||||||
return err
|
return errdefs.System(err)
|
||||||
}
|
}
|
||||||
e := errNoSuchProcess{pid, 9}
|
err = errNoSuchProcess{pid, syscall.SIGKILL}
|
||||||
logrus.WithError(e).WithField("container", container.ID).Debug("no such process")
|
logrus.WithError(err).WithField("container", container.ID).Debug("no such process")
|
||||||
return e
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case there were some exceptions(e.g., state of zombie and D)
|
// In case there were some exceptions(e.g., state of zombie and D)
|
||||||
if system.IsProcessAlive(pid) {
|
if system.IsProcessAlive(pid) {
|
||||||
// Since we can not kill a zombie pid, add zombie check here
|
// Since we can not kill a zombie pid, add zombie check here
|
||||||
isZombie, err := system.IsProcessZombie(pid)
|
isZombie, err := system.IsProcessZombie(pid)
|
||||||
|
// TODO(thaJeztah) should we ignore os.IsNotExist() here? ("/proc/<pid>/stat" will be gone if the process exited)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).WithField("container", container.ID).Warn("Container state is invalid")
|
logrus.WithError(err).WithField("container", container.ID).Warn("Container state is invalid")
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -279,7 +279,7 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, stdin
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
logrus.Debugf("Sending TERM signal to process %v in container %v", name, c.ID)
|
logrus.Debugf("Sending TERM signal to process %v in container %v", name, c.ID)
|
||||||
daemon.containerd.SignalProcess(ctx, c.ID, name, int(signal.SignalMap["TERM"]))
|
daemon.containerd.SignalProcess(ctx, c.ID, name, signal.SignalMap["TERM"])
|
||||||
|
|
||||||
timeout := time.NewTimer(termProcessTimeout)
|
timeout := time.NewTimer(termProcessTimeout)
|
||||||
defer timeout.Stop()
|
defer timeout.Stop()
|
||||||
|
@ -287,7 +287,7 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, stdin
|
||||||
select {
|
select {
|
||||||
case <-timeout.C:
|
case <-timeout.C:
|
||||||
logrus.Infof("Container %v, process %v failed to exit within %v of signal TERM - using the force", c.ID, name, termProcessTimeout)
|
logrus.Infof("Container %v, process %v failed to exit within %v of signal TERM - using the force", c.ID, name, termProcessTimeout)
|
||||||
daemon.containerd.SignalProcess(ctx, c.ID, name, int(signal.SignalMap["KILL"]))
|
daemon.containerd.SignalProcess(ctx, c.ID, name, signal.SignalMap["KILL"])
|
||||||
case <-attachErr:
|
case <-attachErr:
|
||||||
// TERM signal worked
|
// TERM signal worked
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,41 +17,42 @@ import (
|
||||||
|
|
||||||
type errNoSuchProcess struct {
|
type errNoSuchProcess struct {
|
||||||
pid int
|
pid int
|
||||||
signal int
|
signal syscall.Signal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e errNoSuchProcess) Error() string {
|
func (e errNoSuchProcess) Error() string {
|
||||||
return fmt.Sprintf("Cannot kill process (pid=%d) with signal %d: no such process.", e.pid, e.signal)
|
return fmt.Sprintf("cannot kill process (pid=%d) with signal %d: no such process", e.pid, e.signal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (errNoSuchProcess) NotFound() {}
|
func (errNoSuchProcess) NotFound() {}
|
||||||
|
|
||||||
// isErrNoSuchProcess returns true if the error
|
|
||||||
// is an instance of errNoSuchProcess.
|
|
||||||
func isErrNoSuchProcess(err error) bool {
|
|
||||||
_, ok := err.(errNoSuchProcess)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerKill sends signal to the container
|
// ContainerKill sends signal to the container
|
||||||
// If no signal is given (sig 0), then Kill with SIGKILL and wait
|
// If no signal is given, then Kill with SIGKILL and wait
|
||||||
// for the container to exit.
|
// for the container to exit.
|
||||||
// If a signal is given, then just send it to the container and return.
|
// If a signal is given, then just send it to the container and return.
|
||||||
func (daemon *Daemon) ContainerKill(name string, sig uint64) error {
|
func (daemon *Daemon) ContainerKill(name, stopSignal string) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
sig = syscall.SIGKILL
|
||||||
|
)
|
||||||
|
if stopSignal != "" {
|
||||||
|
sig, err = signal.ParseSignal(stopSignal)
|
||||||
|
if err != nil {
|
||||||
|
return errdefs.InvalidParameter(err)
|
||||||
|
}
|
||||||
|
if !signal.ValidSignalForPlatform(sig) {
|
||||||
|
return errdefs.InvalidParameter(errors.Errorf("the %s daemon does not support signal %d", runtime.GOOS, sig))
|
||||||
|
}
|
||||||
|
}
|
||||||
container, err := daemon.GetContainer(name)
|
container, err := daemon.GetContainer(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if sig == syscall.SIGKILL {
|
||||||
if sig != 0 && !signal.ValidSignalForPlatform(syscall.Signal(sig)) {
|
// perform regular Kill (SIGKILL + wait())
|
||||||
return fmt.Errorf("The %s daemon does not support signal %d", runtime.GOOS, sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
|
|
||||||
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
|
|
||||||
return daemon.Kill(container)
|
return daemon.Kill(container)
|
||||||
}
|
}
|
||||||
return daemon.killWithSignal(container, int(sig))
|
return daemon.killWithSignal(container, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// killWithSignal sends the container the given signal. This wrapper for the
|
// killWithSignal sends the container the given signal. This wrapper for the
|
||||||
|
@ -59,8 +60,8 @@ func (daemon *Daemon) ContainerKill(name string, sig uint64) error {
|
||||||
// to send the signal. An error is returned if the container is paused
|
// to send the signal. An error is returned if the container is paused
|
||||||
// or not running, or if there is a problem returned from the
|
// or not running, or if there is a problem returned from the
|
||||||
// underlying kill command.
|
// underlying kill command.
|
||||||
func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int) error {
|
func (daemon *Daemon) killWithSignal(container *containerpkg.Container, stopSignal syscall.Signal) error {
|
||||||
logrus.Debugf("Sending kill signal %d to container %s", sig, container.ID)
|
logrus.Debugf("Sending kill signal %d to container %s", stopSignal, container.ID)
|
||||||
container.Lock()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
@ -69,12 +70,12 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int)
|
||||||
}
|
}
|
||||||
|
|
||||||
var unpause bool
|
var unpause bool
|
||||||
if container.Config.StopSignal != "" && syscall.Signal(sig) != syscall.SIGKILL {
|
if container.Config.StopSignal != "" && stopSignal != syscall.SIGKILL {
|
||||||
containerStopSignal, err := signal.ParseSignal(container.Config.StopSignal)
|
containerStopSignal, err := signal.ParseSignal(container.Config.StopSignal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if containerStopSignal == syscall.Signal(sig) {
|
if containerStopSignal == stopSignal {
|
||||||
container.ExitOnNext()
|
container.ExitOnNext()
|
||||||
unpause = container.Paused
|
unpause = container.Paused
|
||||||
}
|
}
|
||||||
|
@ -95,7 +96,8 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := daemon.kill(container, sig); err != nil {
|
err := daemon.containerd.SignalProcess(context.Background(), container.ID, libcontainerdtypes.InitProcessName, stopSignal)
|
||||||
|
if err != nil {
|
||||||
if errdefs.IsNotFound(err) {
|
if errdefs.IsNotFound(err) {
|
||||||
unpause = false
|
unpause = false
|
||||||
logrus.WithError(err).WithField("container", container.ID).WithField("action", "kill").Debug("container kill failed because of 'container not found' or 'no such process'")
|
logrus.WithError(err).WithField("container", container.ID).WithField("action", "kill").Debug("container kill failed because of 'container not found' or 'no such process'")
|
||||||
|
@ -125,7 +127,7 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int)
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes := map[string]string{
|
attributes := map[string]string{
|
||||||
"signal": fmt.Sprintf("%d", sig),
|
"signal": fmt.Sprintf("%d", stopSignal),
|
||||||
}
|
}
|
||||||
daemon.LogContainerEventWithAttributes(container, "kill", attributes)
|
daemon.LogContainerEventWithAttributes(container, "kill", attributes)
|
||||||
return nil
|
return nil
|
||||||
|
@ -138,9 +140,9 @@ func (daemon *Daemon) Kill(container *containerpkg.Container) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Send SIGKILL
|
// 1. Send SIGKILL
|
||||||
if err := daemon.killPossiblyDeadProcess(container, int(syscall.SIGKILL)); err != nil {
|
if err := daemon.killPossiblyDeadProcess(container, syscall.SIGKILL); err != nil {
|
||||||
// kill failed, check if process is no longer running.
|
// kill failed, check if process is no longer running.
|
||||||
if isErrNoSuchProcess(err) {
|
if errors.As(err, &errNoSuchProcess{}) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +158,7 @@ func (daemon *Daemon) Kill(container *containerpkg.Container) error {
|
||||||
logrus.WithError(status.Err()).WithField("container", container.ID).Error("Container failed to exit within 10 seconds of kill - trying direct SIGKILL")
|
logrus.WithError(status.Err()).WithField("container", container.ID).Error("Container failed to exit within 10 seconds of kill - trying direct SIGKILL")
|
||||||
|
|
||||||
if err := killProcessDirectly(container); err != nil {
|
if err := killProcessDirectly(container); err != nil {
|
||||||
if isErrNoSuchProcess(err) {
|
if errors.As(err, &errNoSuchProcess{}) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -173,16 +175,12 @@ func (daemon *Daemon) Kill(container *containerpkg.Container) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// killPossibleDeadProcess is a wrapper around killSig() suppressing "no such process" error.
|
// killPossibleDeadProcess is a wrapper around killSig() suppressing "no such process" error.
|
||||||
func (daemon *Daemon) killPossiblyDeadProcess(container *containerpkg.Container, sig int) error {
|
func (daemon *Daemon) killPossiblyDeadProcess(container *containerpkg.Container, sig syscall.Signal) error {
|
||||||
err := daemon.killWithSignal(container, sig)
|
err := daemon.killWithSignal(container, sig)
|
||||||
if errdefs.IsNotFound(err) {
|
if errdefs.IsNotFound(err) {
|
||||||
e := errNoSuchProcess{container.GetPID(), sig}
|
err = errNoSuchProcess{container.GetPID(), sig}
|
||||||
logrus.Debug(e)
|
logrus.Debug(err)
|
||||||
return e
|
return err
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) kill(c *containerpkg.Container, sig int) error {
|
|
||||||
return daemon.containerd.SignalProcess(context.Background(), c.ID, libcontainerdtypes.InitProcessName, sig)
|
|
||||||
}
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ func (daemon *Daemon) containerStop(ctx context.Context, ctr *container.Containe
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errdefs.InvalidParameter(err)
|
return errdefs.InvalidParameter(err)
|
||||||
}
|
}
|
||||||
stopSignal = int(sig)
|
stopSignal = sig
|
||||||
}
|
}
|
||||||
if options.Timeout != nil {
|
if options.Timeout != nil {
|
||||||
stopTimeout = *options.Timeout
|
stopTimeout = *options.Timeout
|
||||||
|
|
|
@ -5,6 +5,7 @@ package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
|
@ -35,7 +36,7 @@ func (c *MockContainerdClient) Create(ctx context.Context, containerID string, s
|
||||||
func (c *MockContainerdClient) Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (pid int, err error) {
|
func (c *MockContainerdClient) Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (pid int, err error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
func (c *MockContainerdClient) SignalProcess(ctx context.Context, containerID, processID string, signal int) error {
|
func (c *MockContainerdClient) SignalProcess(ctx context.Context, containerID, processID string, signal syscall.Signal) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (c *MockContainerdClient) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) {
|
func (c *MockContainerdClient) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) {
|
||||||
|
|
|
@ -688,7 +688,7 @@ func (c *client) Exec(ctx context.Context, containerID, processID string, spec *
|
||||||
// SignalProcess handles `docker stop` on Windows. While Linux has support for
|
// SignalProcess handles `docker stop` on Windows. While Linux has support for
|
||||||
// the full range of signals, signals aren't really implemented on Windows.
|
// the full range of signals, signals aren't really implemented on Windows.
|
||||||
// We fake supporting regular stop and -9 to force kill.
|
// We fake supporting regular stop and -9 to force kill.
|
||||||
func (c *client) SignalProcess(_ context.Context, containerID, processID string, signal int) error {
|
func (c *client) SignalProcess(_ context.Context, containerID, processID string, signal syscall.Signal) error {
|
||||||
ctr, p, err := c.getProcess(containerID, processID)
|
ctr, p, err := c.getProcess(containerID, processID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -333,12 +333,12 @@ func (c *client) Exec(ctx context.Context, containerID, processID string, spec *
|
||||||
return int(p.Pid()), nil
|
return int(p.Pid()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) SignalProcess(ctx context.Context, containerID, processID string, signal int) error {
|
func (c *client) SignalProcess(ctx context.Context, containerID, processID string, signal syscall.Signal) error {
|
||||||
p, err := c.getProcess(ctx, containerID, processID)
|
p, err := c.getProcess(ctx, containerID, processID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return wrapError(p.Kill(ctx, syscall.Signal(signal)))
|
return wrapError(p.Kill(ctx, signal))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error {
|
func (c *client) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package types // import "github.com/docker/docker/libcontainerd/types"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
|
@ -54,7 +55,7 @@ type Client interface {
|
||||||
|
|
||||||
Create(ctx context.Context, containerID string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error
|
Create(ctx context.Context, containerID string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error
|
||||||
Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio StdioCallback) (pid int, err error)
|
Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio StdioCallback) (pid int, err error)
|
||||||
SignalProcess(ctx context.Context, containerID, processID string, signal int) error
|
SignalProcess(ctx context.Context, containerID, processID string, signal syscall.Signal) error
|
||||||
Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error)
|
Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error)
|
||||||
ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error
|
ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error
|
||||||
CloseStdin(ctx context.Context, containerID, processID string) error
|
CloseStdin(ctx context.Context, containerID, processID string) error
|
||||||
|
|
|
@ -33,6 +33,7 @@ func IsProcessZombie(pid int) (bool, error) {
|
||||||
statPath := fmt.Sprintf("/proc/%d/stat", pid)
|
statPath := fmt.Sprintf("/proc/%d/stat", pid)
|
||||||
dataBytes, err := os.ReadFile(statPath)
|
dataBytes, err := os.ReadFile(statPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// TODO(thaJeztah) should we ignore os.IsNotExist() here? ("/proc/<pid>/stat" will be gone if the process exited)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
data := string(dataBytes)
|
data := string(dataBytes)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/cio"
|
"github.com/containerd/containerd/cio"
|
||||||
|
@ -112,7 +113,7 @@ func (e *Executor) IsRunning(id string) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal sends the specified signal to the container
|
// Signal sends the specified signal to the container
|
||||||
func (e *Executor) Signal(id string, signal int) error {
|
func (e *Executor) Signal(id string, signal syscall.Signal) error {
|
||||||
return e.client.SignalProcess(context.Background(), id, libcontainerdtypes.InitProcessName, signal)
|
return e.client.SignalProcess(context.Background(), id, libcontainerdtypes.InitProcessName, signal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/content/local"
|
"github.com/containerd/containerd/content/local"
|
||||||
|
@ -37,7 +38,7 @@ type Executor interface {
|
||||||
Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error
|
Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error
|
||||||
IsRunning(id string) (bool, error)
|
IsRunning(id string) (bool, error)
|
||||||
Restore(id string, stdout, stderr io.WriteCloser) (alive bool, err error)
|
Restore(id string, stdout, stderr io.WriteCloser) (alive bool, err error)
|
||||||
Signal(id string, signal int) error
|
Signal(id string, signal syscall.Signal) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointResolver provides looking up registry endpoints for pulling.
|
// EndpointResolver provides looking up registry endpoints for pulling.
|
||||||
|
|
|
@ -154,31 +154,30 @@ const shutdownTimeout = 10 * time.Second
|
||||||
func shutdownPlugin(p *v2.Plugin, ec chan bool, executor Executor) {
|
func shutdownPlugin(p *v2.Plugin, ec chan bool, executor Executor) {
|
||||||
pluginID := p.GetID()
|
pluginID := p.GetID()
|
||||||
|
|
||||||
err := executor.Signal(pluginID, int(unix.SIGTERM))
|
if err := executor.Signal(pluginID, unix.SIGTERM); err != nil {
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
|
logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
|
||||||
} else {
|
return
|
||||||
|
}
|
||||||
|
|
||||||
timeout := time.NewTimer(shutdownTimeout)
|
timeout := time.NewTimer(shutdownTimeout)
|
||||||
defer timeout.Stop()
|
defer timeout.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ec:
|
||||||
|
logrus.Debug("Clean shutdown of plugin")
|
||||||
|
case <-timeout.C:
|
||||||
|
logrus.Debug("Force shutdown plugin")
|
||||||
|
if err := executor.Signal(pluginID, unix.SIGKILL); err != nil {
|
||||||
|
logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout.Reset(shutdownTimeout)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ec:
|
case <-ec:
|
||||||
logrus.Debug("Clean shutdown of plugin")
|
logrus.Debug("SIGKILL plugin shutdown")
|
||||||
case <-timeout.C:
|
case <-timeout.C:
|
||||||
logrus.Debug("Force shutdown plugin")
|
logrus.WithField("plugin", p.Name).Warn("Force shutdown plugin FAILED")
|
||||||
if err := executor.Signal(pluginID, int(unix.SIGKILL)); err != nil {
|
|
||||||
logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
timeout.Reset(shutdownTimeout)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ec:
|
|
||||||
logrus.Debug("SIGKILL plugin shutdown")
|
|
||||||
case <-timeout.C:
|
|
||||||
logrus.WithField("plugin", p.Name).Warn("Force shutdown plugin FAILED")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -87,24 +88,13 @@ func newTestPlugin(t *testing.T, name, cap, root string) *v2.Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
type simpleExecutor struct {
|
type simpleExecutor struct {
|
||||||
|
Executor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *simpleExecutor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error {
|
func (e *simpleExecutor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error {
|
||||||
return errors.New("Create failed")
|
return errors.New("Create failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *simpleExecutor) Restore(id string, stdout, stderr io.WriteCloser) (bool, error) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *simpleExecutor) IsRunning(id string) (bool, error) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *simpleExecutor) Signal(id string, signal int) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateFailed(t *testing.T) {
|
func TestCreateFailed(t *testing.T) {
|
||||||
root, err := os.MkdirTemp("", "test-create-failed")
|
root, err := os.MkdirTemp("", "test-create-failed")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -165,7 +155,7 @@ func (e *executorWithRunning) Restore(id string, stdout, stderr io.WriteCloser)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *executorWithRunning) Signal(id string, signal int) error {
|
func (e *executorWithRunning) Signal(id string, signal syscall.Signal) error {
|
||||||
ch := e.exitChans[id]
|
ch := e.exitChans[id]
|
||||||
ch <- struct{}{}
|
ch <- struct{}{}
|
||||||
<-ch
|
<-ch
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue