diff --git a/cmd/ctr/list.go b/cmd/ctr/list.go index 117796d67..34c138f29 100644 --- a/cmd/ctr/list.go +++ b/cmd/ctr/list.go @@ -6,7 +6,7 @@ import ( "text/tabwriter" "github.com/containerd/containerd" - "github.com/containerd/containerd/api/services/execution" + tasks "github.com/containerd/containerd/api/services/tasks/v1" tasktypes "github.com/containerd/containerd/api/types/task" "github.com/urfave/cli" ) @@ -67,9 +67,9 @@ func taskListFn(context *cli.Context) error { return err } - tasks := client.TaskService() + s := client.TaskService() - tasksResponse, err := tasks.List(ctx, &execution.ListRequest{}) + tasksResponse, err := s.List(ctx, &tasks.ListTasksRequest{}) if err != nil { return err } diff --git a/container.go b/container.go index f9d5706b6..0bcaefc86 100644 --- a/container.go +++ b/container.go @@ -20,6 +20,7 @@ var ( ErrNoImage = errors.New("container does not have an image") ErrNoRunningTask = errors.New("no running task") ErrDeleteRunningTask = errors.New("cannot delete container with running task") + ErrProcessExited = errors.New("process already exited") ) type Container interface { diff --git a/container_test.go b/container_test.go index ed56fb7e5..bcbb35a77 100644 --- a/container_test.go +++ b/container_test.go @@ -665,3 +665,72 @@ func TestDeleteRunningContainer(t *testing.T) { } <-statusC } + +func TestContainerKill(t *testing.T) { + if testing.Short() { + t.Skip() + } + client, err := New(address) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + var ( + ctx, cancel = testContext() + id = t.Name() + ) + defer cancel() + + image, err := client.GetImage(ctx, testImage) + if err != nil { + t.Error(err) + return + } + spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sh", "-c", "cat")) + if err != nil { + t.Error(err) + return + } + container, err := client.NewContainer(ctx, id, WithSpec(spec), WithImage(image), WithNewRootFS(id, image)) + if err != nil { + t.Error(err) + return + } + defer container.Delete(ctx) + + task, err := container.NewTask(ctx, Stdio) + if err != nil { + t.Error(err) + return + } + defer task.Delete(ctx) + + statusC := make(chan uint32, 1) + go func() { + status, err := task.Wait(ctx) + if err != nil { + t.Error(err) + } + statusC <- status + }() + + if err := task.Start(ctx); err != nil { + t.Error(err) + return + } + if err := task.Kill(ctx, syscall.SIGKILL); err != nil { + t.Error(err) + return + } + <-statusC + + err = task.Kill(ctx, syscall.SIGTERM) + if err == nil { + t.Error("second call to kill should return an error") + return + } + if err != ErrProcessExited { + t.Errorf("expected error %q but received %q", ErrProcessExited, err) + } +} diff --git a/linux/shim/exec.go b/linux/shim/exec.go index 03614f303..cc55b21bd 100644 --- a/linux/shim/exec.go +++ b/linux/shim/exec.go @@ -167,7 +167,10 @@ func (e *execProcess) Resize(ws console.WinSize) error { } func (e *execProcess) Signal(sig int) error { - return unix.Kill(e.pid, syscall.Signal(sig)) + if err := unix.Kill(e.pid, syscall.Signal(sig)); err != nil { + return checkKillError(err) + } + return nil } func (e *execProcess) Stdin() io.Closer { diff --git a/linux/shim/init.go b/linux/shim/init.go index 3c6d3228a..47ef38cb2 100644 --- a/linux/shim/init.go +++ b/linux/shim/init.go @@ -20,6 +20,7 @@ import ( shimapi "github.com/containerd/containerd/api/services/shim/v1" "github.com/containerd/containerd/log" "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/plugin" "github.com/containerd/fifo" runc "github.com/containerd/go-runc" "github.com/pkg/errors" @@ -234,7 +235,7 @@ func (p *initProcess) Kill(context context.Context, signal uint32, all bool) err err := p.runc.Kill(context, p.id, int(signal), &runc.KillOpts{ All: all, }) - return p.runcError(err, "runc kill failed") + return checkKillError(err) } func (p *initProcess) killAll(context context.Context) error { @@ -245,7 +246,7 @@ func (p *initProcess) killAll(context context.Context) error { } func (p *initProcess) Signal(sig int) error { - return unix.Kill(p.pid, syscall.Signal(sig)) + return checkKillError(unix.Kill(p.pid, syscall.Signal(sig))) } func (p *initProcess) Stdin() io.Closer { @@ -352,3 +353,13 @@ func copyFile(to, from string) error { _, err = io.Copy(tt, ff) return err } + +func checkKillError(err error) error { + if err == nil { + return nil + } + if strings.Contains(err.Error(), "os: process already finished") || err == unix.ESRCH { + return plugin.ErrProcessExited + } + return err +} diff --git a/linux/shim/service.go b/linux/shim/service.go index 17150e6aa..e4a8f89bb 100644 --- a/linux/shim/service.go +++ b/linux/shim/service.go @@ -274,12 +274,16 @@ func (s *Service) Kill(ctx context.Context, r *shimapi.KillRequest) (*google_pro } return empty, nil } - + if int(r.Pid) == s.initProcess.pid { + if err := s.initProcess.Kill(ctx, r.Signal, r.All); err != nil { + return nil, err + } + return empty, nil + } pids, err := s.getContainerPids(ctx, s.initProcess.id) if err != nil { return nil, err } - valid := false for _, p := range pids { if r.Pid == p { @@ -287,15 +291,12 @@ func (s *Service) Kill(ctx context.Context, r *shimapi.KillRequest) (*google_pro break } } - if !valid { return nil, errors.Errorf("process %d does not exist in container", r.Pid) } - if err := unix.Kill(int(r.Pid), syscall.Signal(r.Signal)); err != nil { - return nil, err + return nil, checkKillError(err) } - return empty, nil } diff --git a/plugin/errors.go b/plugin/errors.go index ac2003ead..bb402716e 100644 --- a/plugin/errors.go +++ b/plugin/errors.go @@ -3,7 +3,8 @@ package plugin import "errors" var ( - ErrContainerExists = errors.New("container with id already exists") - ErrContainerNotExist = errors.New("container does not exist") - ErrRuntimeNotExist = errors.New("runtime does not exist") + ErrContainerExists = errors.New("runtime: container with id already exists") + ErrContainerNotExist = errors.New("runtime: container does not exist") + ErrRuntimeNotExist = errors.New("runtime: runtime does not exist") + ErrProcessExited = errors.New("runtime: process already exited") ) diff --git a/task.go b/task.go index 800d23cd4..d00e63d4a 100644 --- a/task.go +++ b/task.go @@ -16,9 +16,11 @@ import ( tasktypes "github.com/containerd/containerd/api/types/task" "github.com/containerd/containerd/content" "github.com/containerd/containerd/events" + "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/rootfs" "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/runtime-spec/specs-go" + "google.golang.org/grpc" ) const UnknownExitStatus = 255 @@ -108,11 +110,17 @@ func (t *task) Kill(ctx context.Context, s syscall.Signal) error { _, err := t.client.TaskService().Kill(ctx, &tasks.KillRequest{ Signal: uint32(s), ContainerID: t.containerID, - PidOrAll: &tasks.KillRequest_All{ - All: true, + PidOrAll: &tasks.KillRequest_Pid{ + Pid: t.pid, }, }) - return err + if err != nil { + if strings.Contains(grpc.ErrorDesc(err), plugin.ErrProcessExited.Error()) { + return ErrProcessExited + } + return err + } + return nil } func (t *task) Pause(ctx context.Context) error {