package daemon import ( "context" "syscall" "testing" "time" containerd "github.com/containerd/containerd/v2/client" cerrdefs "github.com/containerd/errdefs" containertypes "github.com/moby/moby/api/types/container" "github.com/moby/moby/v2/daemon/container" "github.com/moby/moby/v2/daemon/events" libcontainerdtypes "github.com/moby/moby/v2/daemon/internal/libcontainerd/types" "gotest.tools/v3/assert" ) type notFoundKillTask struct { libcontainerdtypes.Task deleteCalled chan struct{} } type killContextKey struct{} type contextKillTask struct { libcontainerdtypes.Task killContext chan context.Context } func (t *contextKillTask) Pid() uint32 { return 1 } func (t *contextKillTask) Kill(ctx context.Context, _ syscall.Signal) error { t.killContext <- ctx return nil } func (t *notFoundKillTask) Pid() uint32 { return 1 } func (t *notFoundKillTask) Kill(context.Context, syscall.Signal) error { return cerrdefs.ErrNotFound } func (t *notFoundKillTask) Delete(ctx context.Context) (*containerd.ExitStatus, error) { close(t.deleteCalled) <-ctx.Done() return nil, ctx.Err() } func TestKillWithSignalWaitsIndefinitelyForDelayedExit(t *testing.T) { task := ¬FoundKillTask{deleteCalled: make(chan struct{})} ctr := container.NewBaseContainer(t.Name(), t.TempDir()) stopTimeout := -1 ctr.Config = &containertypes.Config{StopTimeout: &stopTimeout} ctr.HostConfig = &containertypes.HostConfig{} ctr.Lock() ctr.State.SetRunning(nil, task, time.Now()) ctr.Unlock() daemon := &Daemon{ EventsService: events.New(), shutdown: true, } err := daemon.killWithSignal(t.Context(), ctr, syscall.SIGKILL) assert.NilError(t, err) select { case <-task.deleteCalled: t.Fatal("fallback exit handling ran before the delayed exit event") case <-time.After(100 * time.Millisecond): } ctr.Lock() ctr.State.SetStopped(&container.ExitStatus{}) ctr.Unlock() } func TestKillWithSignalDetachesContext(t *testing.T) { task := &contextKillTask{killContext: make(chan context.Context, 1)} ctr := container.NewBaseContainer(t.Name(), t.TempDir()) ctr.Config = &containertypes.Config{} ctr.HostConfig = &containertypes.HostConfig{} ctr.Lock() ctr.State.SetRunning(nil, task, time.Now()) ctr.Unlock() daemon := &Daemon{ EventsService: events.New(), shutdown: true, } const contextValue = "value" ctx := context.WithValue(t.Context(), killContextKey{}, contextValue) ctx, cancel := context.WithCancel(ctx) cancel() err := daemon.killWithSignal(ctx, ctr, syscall.SIGKILL) assert.NilError(t, err) killCtx := <-task.killContext assert.NilError(t, killCtx.Err()) assert.Equal(t, killCtx.Value(killContextKey{}), contextValue) }