Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
| ... | ... |
@@ -8,6 +8,8 @@ import ( |
| 8 | 8 |
"fmt" |
| 9 | 9 |
"io/ioutil" |
| 10 | 10 |
"net/http" |
| 11 |
+ "os" |
|
| 12 |
+ "strings" |
|
| 11 | 13 |
"time" |
| 12 | 14 |
|
| 13 | 15 |
"github.com/docker/docker/api/types" |
| ... | ... |
@@ -198,6 +200,45 @@ func (s *DockerSuite) TestExecAPIStartInvalidCommand(c *check.C) {
|
| 198 | 198 |
c.Assert(inspectJSON.ExecIDs, checker.IsNil) |
| 199 | 199 |
} |
| 200 | 200 |
|
| 201 |
+func (s *DockerSuite) TestExecStateCleanup(c *check.C) {
|
|
| 202 |
+ testRequires(c, DaemonIsLinux, SameHostDaemon) |
|
| 203 |
+ |
|
| 204 |
+ // This test checks accidental regressions. Not part of stable API. |
|
| 205 |
+ |
|
| 206 |
+ name := "exec_cleanup" |
|
| 207 |
+ cid, _ := dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh") |
|
| 208 |
+ cid = strings.TrimSpace(cid) |
|
| 209 |
+ |
|
| 210 |
+ stateDir := "/var/run/docker/containerd/" + cid |
|
| 211 |
+ |
|
| 212 |
+ checkReadDir := func(c *check.C) (interface{}, check.CommentInterface) {
|
|
| 213 |
+ fi, err := ioutil.ReadDir(stateDir) |
|
| 214 |
+ c.Assert(err, checker.IsNil) |
|
| 215 |
+ return len(fi), nil |
|
| 216 |
+ } |
|
| 217 |
+ |
|
| 218 |
+ fi, err := ioutil.ReadDir(stateDir) |
|
| 219 |
+ c.Assert(err, checker.IsNil) |
|
| 220 |
+ c.Assert(len(fi), checker.GreaterThan, 1) |
|
| 221 |
+ |
|
| 222 |
+ id := createExecCmd(c, name, "ls") |
|
| 223 |
+ startExec(c, id, http.StatusOK) |
|
| 224 |
+ waitForExec(c, id) |
|
| 225 |
+ |
|
| 226 |
+ waitAndAssert(c, 5*time.Second, checkReadDir, checker.Equals, len(fi)) |
|
| 227 |
+ |
|
| 228 |
+ id = createExecCmd(c, name, "invalid") |
|
| 229 |
+ startExec(c, id, http.StatusBadRequest) |
|
| 230 |
+ waitForExec(c, id) |
|
| 231 |
+ |
|
| 232 |
+ waitAndAssert(c, 5*time.Second, checkReadDir, checker.Equals, len(fi)) |
|
| 233 |
+ |
|
| 234 |
+ dockerCmd(c, "stop", name) |
|
| 235 |
+ _, err = os.Stat(stateDir) |
|
| 236 |
+ c.Assert(err, checker.NotNil) |
|
| 237 |
+ c.Assert(os.IsNotExist(err), checker.True) |
|
| 238 |
+} |
|
| 239 |
+ |
|
| 201 | 240 |
func createExec(c *check.C, name string) string {
|
| 202 | 241 |
return createExecCmd(c, name, "true") |
| 203 | 242 |
} |
| ... | ... |
@@ -28,7 +28,7 @@ import ( |
| 28 | 28 |
"github.com/containerd/typeurl" |
| 29 | 29 |
"github.com/docker/docker/pkg/ioutils" |
| 30 | 30 |
"github.com/opencontainers/image-spec/specs-go/v1" |
| 31 |
- "github.com/opencontainers/runtime-spec/specs-go" |
|
| 31 |
+ specs "github.com/opencontainers/runtime-spec/specs-go" |
|
| 32 | 32 |
"github.com/pkg/errors" |
| 33 | 33 |
"github.com/sirupsen/logrus" |
| 34 | 34 |
) |
| ... | ... |
@@ -202,7 +202,8 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin |
| 202 | 202 |
uid, gid := getSpecUser(spec) |
| 203 | 203 |
t, err = ctr.ctr.NewTask(ctx, |
| 204 | 204 |
func(id string) (containerd.IO, error) {
|
| 205 |
- cio, err = c.createIO(ctr.bundleDir, id, InitProcessName, stdinCloseSync, withStdin, spec.Process.Terminal, attachStdio) |
|
| 205 |
+ fifos := newFIFOSet(ctr.bundleDir, id, InitProcessName, withStdin, spec.Process.Terminal) |
|
| 206 |
+ cio, err = c.createIO(fifos, id, InitProcessName, stdinCloseSync, attachStdio) |
|
| 206 | 207 |
return cio, err |
| 207 | 208 |
}, |
| 208 | 209 |
func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error {
|
| ... | ... |
@@ -260,17 +261,21 @@ func (c *client) Exec(ctx context.Context, containerID, processID string, spec * |
| 260 | 260 |
err error |
| 261 | 261 |
stdinCloseSync = make(chan struct{})
|
| 262 | 262 |
) |
| 263 |
+ |
|
| 264 |
+ fifos := newFIFOSet(ctr.bundleDir, containerID, processID, withStdin, spec.Terminal) |
|
| 265 |
+ |
|
| 263 | 266 |
defer func() {
|
| 264 | 267 |
if err != nil {
|
| 265 | 268 |
if cio != nil {
|
| 266 | 269 |
cio.Cancel() |
| 267 | 270 |
cio.Close() |
| 268 | 271 |
} |
| 272 |
+ rmFIFOSet(fifos) |
|
| 269 | 273 |
} |
| 270 | 274 |
}() |
| 271 | 275 |
|
| 272 | 276 |
p, err = ctr.task.Exec(ctx, processID, spec, func(id string) (containerd.IO, error) {
|
| 273 |
- cio, err = c.createIO(ctr.bundleDir, containerID, processID, stdinCloseSync, withStdin, spec.Terminal, attachStdio) |
|
| 277 |
+ cio, err = c.createIO(fifos, containerID, processID, stdinCloseSync, attachStdio) |
|
| 274 | 278 |
return cio, err |
| 275 | 279 |
}) |
| 276 | 280 |
if err != nil {
|
| ... | ... |
@@ -441,7 +446,7 @@ func (c *client) Delete(ctx context.Context, containerID string) error {
|
| 441 | 441 |
return err |
| 442 | 442 |
} |
| 443 | 443 |
|
| 444 |
- if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" {
|
|
| 444 |
+ if os.Getenv("LIBCONTAINERD_NOCLEAN") != "1" {
|
|
| 445 | 445 |
if err := os.RemoveAll(ctr.bundleDir); err != nil {
|
| 446 | 446 |
c.logger.WithError(err).WithFields(logrus.Fields{
|
| 447 | 447 |
"container": containerID, |
| ... | ... |
@@ -562,8 +567,7 @@ func (c *client) getProcess(containerID, processID string) (containerd.Process, |
| 562 | 562 |
|
| 563 | 563 |
// createIO creates the io to be used by a process |
| 564 | 564 |
// This needs to get a pointer to interface as upon closure the process may not have yet been registered |
| 565 |
-func (c *client) createIO(bundleDir, containerID, processID string, stdinCloseSync chan struct{}, withStdin, withTerminal bool, attachStdio StdioCallback) (containerd.IO, error) {
|
|
| 566 |
- fifos := newFIFOSet(bundleDir, containerID, processID, withStdin, withTerminal) |
|
| 565 |
+func (c *client) createIO(fifos *containerd.FIFOSet, containerID, processID string, stdinCloseSync chan struct{}, attachStdio StdioCallback) (containerd.IO, error) {
|
|
| 567 | 566 |
io, err := newIOPipe(fifos) |
| 568 | 567 |
if err != nil {
|
| 569 | 568 |
return nil, err |
| ... | ... |
@@ -639,6 +643,14 @@ func (c *client) processEvent(ctr *container, et EventType, ei EventInfo) {
|
| 639 | 639 |
c.Lock() |
| 640 | 640 |
delete(ctr.execs, ei.ProcessID) |
| 641 | 641 |
c.Unlock() |
| 642 |
+ ctr := c.getContainer(ei.ContainerID) |
|
| 643 |
+ if ctr == nil {
|
|
| 644 |
+ c.logger.WithFields(logrus.Fields{
|
|
| 645 |
+ "container": ei.ContainerID, |
|
| 646 |
+ }).Error("failed to find container")
|
|
| 647 |
+ } else {
|
|
| 648 |
+ rmFIFOSet(newFIFOSet(ctr.bundleDir, ei.ContainerID, ei.ProcessID, true, false)) |
|
| 649 |
+ } |
|
| 642 | 650 |
} |
| 643 | 651 |
}) |
| 644 | 652 |
} |
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
"github.com/containerd/containerd" |
| 11 | 11 |
"github.com/docker/docker/pkg/idtools" |
| 12 | 12 |
specs "github.com/opencontainers/runtime-spec/specs-go" |
| 13 |
+ "github.com/sirupsen/logrus" |
|
| 13 | 14 |
) |
| 14 | 15 |
|
| 15 | 16 |
func summaryFromInterface(i interface{}) (*Summary, error) {
|
| ... | ... |
@@ -94,3 +95,13 @@ func newFIFOSet(bundleDir, containerID, processID string, withStdin, withTermina |
| 94 | 94 |
|
| 95 | 95 |
return fifos |
| 96 | 96 |
} |
| 97 |
+ |
|
| 98 |
+func rmFIFOSet(fset *containerd.FIFOSet) {
|
|
| 99 |
+ for _, fn := range []string{fset.Out, fset.In, fset.Err} {
|
|
| 100 |
+ if fn != "" {
|
|
| 101 |
+ if err := os.RemoveAll(fn); err != nil {
|
|
| 102 |
+ logrus.Warnf("libcontainerd: failed to remove fifo %v: %v", fn, err)
|
|
| 103 |
+ } |
|
| 104 |
+ } |
|
| 105 |
+ } |
|
| 106 |
+} |