Skip to content

Commit 2578f3d

Browse files
authored
Merge pull request #225 from kzys/fix-65
Make sure containers are deleted correctly
2 parents 46e4f46 + 118e9c6 commit 2578f3d

File tree

2 files changed

+152
-3
lines changed

2 files changed

+152
-3
lines changed

runtime/service.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ type service struct {
124124
agentClient taskAPI.TaskService
125125
eventBridgeClient eventbridge.Getter
126126
stubDriveHandler stubDriveHandler
127-
exitAfterAllTasksDeleted bool
127+
exitAfterAllTasksDeleted bool // exit the VM and shim when all tasks are deleted
128128

129129
machine *firecracker.Machine
130130
machineConfig *firecracker.Config
@@ -284,7 +284,8 @@ func (s *service) StartShim(shimCtx context.Context, containerID, containerdBina
284284

285285
logrus.SetOutput(logFifo)
286286

287-
log.G(shimCtx).WithField("id", containerID).Debug("StartShim")
287+
log := log.G(shimCtx).WithField("id", containerID)
288+
log.Debug("StartShim")
288289

289290
// If we are running a shim start routine, we can safely assume our current working
290291
// directory is the bundle directory
@@ -319,6 +320,10 @@ func (s *service) StartShim(shimCtx context.Context, containerID, containerdBina
319320
// task is deleted
320321
containerCount = 1
321322
exitAfterAllTasksDeleted = true
323+
324+
log.Infof("will start a single-task VM %s since no VMID has been provided", s.vmID)
325+
} else {
326+
log.Infof("will start a persistent VM %s", s.vmID)
322327
}
323328

324329
client, err := ttrpcutil.NewClient(containerdAddress + ".ttrpc")
@@ -773,13 +778,36 @@ func (s *service) Start(requestCtx context.Context, req *taskAPI.StartRequest) (
773778

774779
func (s *service) Delete(requestCtx context.Context, req *taskAPI.DeleteRequest) (*taskAPI.DeleteResponse, error) {
775780
defer logPanicAndDie(log.G(requestCtx))
776-
log.G(requestCtx).WithFields(logrus.Fields{"id": req.ID, "exec_id": req.ExecID}).Debug("delete")
781+
logger := log.G(requestCtx).WithFields(logrus.Fields{"id": req.ID, "exec_id": req.ExecID})
782+
783+
logger.Debug("delete")
777784

778785
resp, err := s.taskManager.DeleteProcess(requestCtx, req, s.agentClient)
779786
if err != nil {
780787
return nil, err
781788
}
782789

790+
// Only delete a process as like runc when there is ExecID
791+
// https://github.com/containerd/containerd/blob/f3e148b1ccf268450c87427b5dbb6187db3d22f1/runtime/v2/runc/container.go#L320
792+
if req.ExecID != "" {
793+
return resp, nil
794+
}
795+
796+
// Otherwise, delete the container
797+
dir, err := s.shimDir.BundleLink(req.ID)
798+
if err != nil {
799+
return nil, errors.Wrapf(err, "failed to find the bundle directory of the container: %s", req.ID)
800+
}
801+
802+
_, err = os.Stat(dir.RootPath())
803+
if os.IsNotExist(err) {
804+
return nil, errors.Wrapf(err, "failed to find the bundle directory of the container: %s", dir.RootPath())
805+
}
806+
807+
if err = os.Remove(dir.RootPath()); err != nil {
808+
return nil, errors.Wrapf(err, "failed to remove the bundle directory of the container: %s", dir.RootPath())
809+
}
810+
783811
return resp, nil
784812
}
785813

runtime/service_integ_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"fmt"
2020
"io/ioutil"
21+
"os"
2122
"os/exec"
2223
"path/filepath"
2324
"strconv"
@@ -533,3 +534,123 @@ vdf 254:80 0 512B 0 disk`
533534
"context cancelled while waiting for container %s to exit, err: %v", containerName, ctx.Err())
534535
}
535536
}
537+
538+
func startAndWaitTask(ctx context.Context, t *testing.T, c containerd.Container) string {
539+
var stdout bytes.Buffer
540+
var stderr bytes.Buffer
541+
542+
task, err := c.NewTask(ctx, cio.NewCreator(cio.WithStreams(nil, &stdout, &stderr)))
543+
require.NoError(t, err, "failed to create task for container %s", c.ID())
544+
545+
exitCh, err := task.Wait(ctx)
546+
require.NoError(t, err, "failed to wait on task for container %s", c.ID())
547+
548+
err = task.Start(ctx)
549+
require.NoError(t, err, "failed to start task for container %s", c.ID())
550+
defer func() {
551+
status, err := task.Delete(ctx)
552+
require.NoError(t, status.Error())
553+
require.NoError(t, err, "failed to delete task for container %s", c.ID())
554+
}()
555+
556+
select {
557+
case exitStatus := <-exitCh:
558+
require.NoError(t, exitStatus.Error(), "failed to retrieve exitStatus")
559+
require.Equal(t, uint32(0), exitStatus.ExitCode())
560+
require.Equal(t, "", stderr.String())
561+
case <-ctx.Done():
562+
require.Fail(t, "context cancelled",
563+
"context cancelled while waiting for container %s to exit, err: %v", c.ID(), ctx.Err())
564+
}
565+
566+
return stdout.String()
567+
}
568+
569+
func testCreateContainerWithSameName(t *testing.T, vmID string) {
570+
ctx := namespaces.WithNamespace(context.Background(), "default")
571+
572+
pluginClient, err := ttrpcutil.NewClient(containerdSockPath + ".ttrpc")
573+
require.NoError(t, err, "failed to create ttrpc client")
574+
575+
// Explicitly specify Container Count = 2 to workaround #230
576+
if len(vmID) != 0 {
577+
fcClient := fccontrol.NewFirecrackerClient(pluginClient.Client())
578+
_, err = fcClient.CreateVM(ctx, &proto.CreateVMRequest{
579+
VMID: vmID,
580+
RootDrive: &proto.FirecrackerDrive{
581+
PathOnHost: defaultRootfsPath,
582+
IsReadOnly: true,
583+
IsRootDevice: true,
584+
},
585+
ContainerCount: 2,
586+
})
587+
require.NoError(t, err)
588+
}
589+
590+
withNewSpec := containerd.WithNewSpec(oci.WithProcessArgs("echo", "hello"), firecrackeroci.WithVMID(vmID))
591+
592+
client, err := containerd.New(containerdSockPath, containerd.WithDefaultRuntime(firecrackerRuntime))
593+
require.NoError(t, err, "unable to create client to containerd service at %s, is containerd running?", containerdSockPath)
594+
defer client.Close()
595+
596+
image, err := client.Pull(ctx, debianDockerImage, containerd.WithPullUnpack, containerd.WithPullSnapshotter(naiveSnapshotterName))
597+
require.NoError(t, err, "failed to pull image %s, is the the %s snapshotter running?", debianDockerImage, naiveSnapshotterName)
598+
599+
containerName := fmt.Sprintf("%s-%d", t.Name(), time.Now().UnixNano())
600+
snapshotName := fmt.Sprintf("%s-snapshot", containerName)
601+
602+
containerPath := fmt.Sprintf("/run/containerd/io.containerd.runtime.v2.task/default/%s", containerName)
603+
604+
c1, err := client.NewContainer(ctx,
605+
containerName,
606+
containerd.WithSnapshotter(naiveSnapshotterName),
607+
containerd.WithNewSnapshot(snapshotName, image),
608+
withNewSpec,
609+
)
610+
require.NoError(t, err, "failed to create container %s", containerName)
611+
require.Equal(t, "hello\n", startAndWaitTask(ctx, t, c1))
612+
613+
// All resources regarding the container will be deleted
614+
err = c1.Delete(ctx, containerd.WithSnapshotCleanup)
615+
require.NoError(t, err, "failed to delete container %s", containerName)
616+
617+
_, err = os.Stat(containerPath)
618+
require.True(t, os.IsNotExist(err))
619+
620+
if len(vmID) != 0 {
621+
shimPath := fmt.Sprintf("%s/default/%s/%s", varRunDir, vmID, containerName)
622+
_, err = os.Stat(shimPath)
623+
require.True(t, os.IsNotExist(err))
624+
}
625+
626+
// So, we can launch a new container with the same name
627+
c2, err := client.NewContainer(ctx,
628+
containerName,
629+
containerd.WithSnapshotter(naiveSnapshotterName),
630+
containerd.WithNewSnapshot(snapshotName, image),
631+
withNewSpec,
632+
)
633+
require.NoError(t, err, "failed to create container %s", containerName)
634+
require.Equal(t, "hello\n", startAndWaitTask(ctx, t, c2))
635+
636+
err = c2.Delete(ctx, containerd.WithSnapshotCleanup)
637+
require.NoError(t, err, "failed to delete container %s", containerName)
638+
639+
_, err = os.Stat(containerPath)
640+
require.True(t, os.IsNotExist(err))
641+
642+
if len(vmID) != 0 {
643+
shimPath := fmt.Sprintf("%s/default/%s/%s", varRunDir, vmID, containerName)
644+
_, err = os.Stat(shimPath)
645+
require.True(t, os.IsNotExist(err))
646+
}
647+
}
648+
649+
func TestCreateContainerWithSameName_Isolated(t *testing.T) {
650+
internal.RequiresIsolation(t)
651+
652+
testCreateContainerWithSameName(t, "")
653+
654+
vmID := fmt.Sprintf("same-vm-%d", time.Now().UnixNano())
655+
testCreateContainerWithSameName(t, vmID)
656+
}

0 commit comments

Comments
 (0)