diff --git a/container_test.go b/container_test.go index a05c09cb1..3844c00bc 100644 --- a/container_test.go +++ b/container_test.go @@ -1451,3 +1451,82 @@ func TestContainerExitedAtSet(t *testing.T) { return } } + +func TestDeleteContainerExecCreated(t *testing.T) { + t.Parallel() + + client, err := newClient(t, address) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + var ( + image Image + ctx, cancel = testContext() + id = t.Name() + ) + defer cancel() + + if runtime.GOOS != "windows" { + image, err = client.GetImage(ctx, testImage) + if err != nil { + t.Error(err) + return + } + } + + spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("sleep", "100")) + if err != nil { + t.Error(err) + return + } + container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewSnapshot(id, image)) + if err != nil { + t.Error(err) + return + } + defer container.Delete(ctx, WithSnapshotCleanup) + + task, err := container.NewTask(ctx, empty()) + if err != nil { + t.Error(err) + return + } + defer task.Delete(ctx) + + finished := make(chan struct{}, 1) + go func() { + if _, err := task.Wait(ctx); err != nil { + t.Error(err) + } + close(finished) + }() + + if err := task.Start(ctx); err != nil { + t.Error(err) + return + } + + // start an exec process without running the original container process info + processSpec := spec.Process + withExecExitStatus(processSpec, 6) + execID := t.Name() + "_exec" + process, err := task.Exec(ctx, execID, processSpec, empty()) + if err != nil { + t.Error(err) + return + } + deleteStatus, err := process.Delete(ctx) + if err != nil { + t.Error(err) + return + } + if deleteStatus != 0 { + t.Errorf("expected delete exit code 0 but received %d", deleteStatus) + } + if err := task.Kill(ctx, syscall.SIGKILL); err != nil { + t.Error(err) + } + <-finished +} diff --git a/process.go b/process.go index aed931eba..620d1a225 100644 --- a/process.go +++ b/process.go @@ -152,7 +152,8 @@ func (p *process) Delete(ctx context.Context, opts ...ProcessDeleteOpts) (uint32 if err != nil { return UnknownExitStatus, err } - if status.Status != Stopped { + switch status.Status { + case Running, Paused, Pausing: return UnknownExitStatus, errors.Wrapf(errdefs.ErrFailedPrecondition, "process must be stopped before deletion") } if p.io != nil { diff --git a/windows/process.go b/windows/process.go index 6aaa9b710..0fe7e2bfd 100644 --- a/windows/process.go +++ b/windows/process.go @@ -59,6 +59,9 @@ func (p *process) Status() runtime.Status { case <-p.exitCh: status = runtime.StoppedStatus default: + if p.hcs == nil { + return runtime.CreatedStatus + } status = runtime.RunningStatus } return status @@ -83,8 +86,8 @@ func (p *process) Pid() uint32 { } func (p *process) ExitCode() (uint32, time.Time, error) { - if p.Status() != runtime.StoppedStatus { - return 255, time.Time{}, errors.Wrap(errdefs.ErrFailedPrecondition, "process is not stopped") + if s := p.Status(); s != runtime.StoppedStatus && s != runtime.CreatedStatus { + return 255, time.Time{}, errors.Wrapf(errdefs.ErrFailedPrecondition, "process is not stopped: %s", s) } return p.exitCode, p.exitTime, nil }