1
0
Fork 0
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:
Sebastiaan van Stijn 2022-05-06 16:35:07 +02:00 committed by GitHub
commit b3675e1839
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 90 additions and 107 deletions

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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:

View file

@ -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 {

View file

@ -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.

View file

@ -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)
} }

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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
} }

View file

@ -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)
}

View file

@ -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

View file

@ -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) {

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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)

View file

@ -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)
} }

View file

@ -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.

View file

@ -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")
}
} }
} }
} }

View file

@ -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