Forcibly stop running containers before removal

Signed-off-by: Abhishek Kulkarni <abd.kulkarni@gmail.com>
This commit is contained in:
Abhishek Kulkarni 2019-04-08 23:21:34 -07:00 committed by Mike Brown
parent aa0f4fd37b
commit 287c52d1c6
4 changed files with 35 additions and 53 deletions

View File

@ -34,48 +34,6 @@ import (
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
)
func TestSandboxCleanRemove(t *testing.T) {
ctx := context.Background()
t.Logf("Create a sandbox")
sbConfig := PodSandboxConfig("sandbox", "clean-remove")
sb, err := runtimeService.RunPodSandbox(sbConfig, *runtimeHandler)
require.NoError(t, err)
defer func() {
// Make sure the sandbox is cleaned up in any case.
runtimeService.StopPodSandbox(sb)
runtimeService.RemovePodSandbox(sb)
}()
t.Logf("Should not be able to remove the sandbox when it's still running")
assert.Error(t, runtimeService.RemovePodSandbox(sb))
t.Logf("Delete sandbox task from containerd")
cntr, err := containerdClient.LoadContainer(ctx, sb)
require.NoError(t, err)
task, err := cntr.Task(ctx, nil)
require.NoError(t, err)
// Kill the task with signal SIGKILL, once the task exited,
// the TaskExit event will trigger the task.Delete().
err = task.Kill(ctx, syscall.SIGKILL, containerd.WithKillAll)
require.NoError(t, err)
t.Logf("Sandbox state should be NOTREADY")
assert.NoError(t, Eventually(func() (bool, error) {
status, err := runtimeService.PodSandboxStatus(sb)
if err != nil {
return false, err
}
return status.GetState() == runtime.PodSandboxState_SANDBOX_NOTREADY, nil
}, time.Second, 30*time.Second), "sandbox state should become NOTREADY")
t.Logf("Should not be able to remove the sandbox when netns is not closed")
assert.Error(t, runtimeService.RemovePodSandbox(sb))
t.Logf("Should be able to remove the sandbox after properly stopped")
assert.NoError(t, runtimeService.StopPodSandbox(sb))
assert.NoError(t, runtimeService.RemovePodSandbox(sb))
}
func TestSandboxRemoveWithoutIPLeakage(t *testing.T) {
const hostLocalCheckpointDir = "/var/lib/cni"

View File

@ -21,6 +21,7 @@ import (
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
@ -29,7 +30,6 @@ import (
)
// RemoveContainer removes the container.
// TODO(random-liu): Forcibly stop container if it's running.
func (c *criService) RemoveContainer(ctx context.Context, r *runtime.RemoveContainerRequest) (_ *runtime.RemoveContainerResponse, retErr error) {
container, err := c.containerStore.Get(r.GetContainerId())
if err != nil {
@ -42,6 +42,17 @@ func (c *criService) RemoveContainer(ctx context.Context, r *runtime.RemoveConta
}
id := container.ID
// Forcibly stop the containers if they are in running or unknown state
state := container.Status.Get().State()
if state == runtime.ContainerState_CONTAINER_RUNNING ||
state == runtime.ContainerState_CONTAINER_UNKNOWN {
logrus.Infof("Forcibly stopping container %q", id)
if err := c.stopContainer(ctx, container, 0); err != nil {
return nil, errors.Wrapf(err, "failed to forcibly stop container %q", id)
}
}
// Set removing state to prevent other start/remove operations against this container
// while it's being removed.
if err := setContainerRemoving(container); err != nil {

View File

@ -21,6 +21,7 @@ import (
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
@ -48,7 +49,10 @@ func (c *criService) RemovePodSandbox(ctx context.Context, r *runtime.RemovePodS
// Return error if sandbox container is still running or unknown.
state := sandbox.Status.Get().State
if state == sandboxstore.StateReady || state == sandboxstore.StateUnknown {
return nil, errors.Errorf("sandbox container %q is not fully stopped", id)
logrus.Infof("Forcibly stopping sandbox %q", id)
if err := c.stopPodSandbox(ctx, sandbox); err != nil {
return nil, errors.Wrapf(err, "failed to forcibly stop sandbox %q", id)
}
}
// Return error if sandbox network namespace is not closed yet.

View File

@ -39,6 +39,15 @@ func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandb
return nil, errors.Wrapf(err, "an error occurred when try to find sandbox %q",
r.GetPodSandboxId())
}
if err := c.stopPodSandbox(ctx, sandbox); err != nil {
return nil, err
}
return &runtime.StopPodSandboxResponse{}, nil
}
func (c *criService) stopPodSandbox(ctx context.Context, sandbox sandboxstore.Sandbox) error {
// Use the full sandbox id.
id := sandbox.ID
@ -52,20 +61,20 @@ func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandb
}
// Forcibly stop the container. Do not use `StopContainer`, because it introduces a race
// if a container is removed after list.
if err = c.stopContainer(ctx, container, 0); err != nil {
return nil, errors.Wrapf(err, "failed to stop container %q", container.ID)
if err := c.stopContainer(ctx, container, 0); err != nil {
return errors.Wrapf(err, "failed to stop container %q", container.ID)
}
}
if err := c.cleanupSandboxFiles(id, sandbox.Config); err != nil {
return nil, errors.Wrap(err, "failed to cleanup sandbox files")
return errors.Wrap(err, "failed to cleanup sandbox files")
}
// Only stop sandbox container when it's running or unknown.
state := sandbox.Status.Get().State
if state == sandboxstore.StateReady || state == sandboxstore.StateUnknown {
if err := c.stopSandboxContainer(ctx, sandbox); err != nil {
return nil, errors.Wrapf(err, "failed to stop sandbox container %q in %q state", id, state)
return errors.Wrapf(err, "failed to stop sandbox container %q in %q state", id, state)
}
}
@ -74,21 +83,21 @@ func (c *criService) StopPodSandbox(ctx context.Context, r *runtime.StopPodSandb
// Use empty netns path if netns is not available. This is defined in:
// https://github.com/containernetworking/cni/blob/v0.7.0-alpha1/SPEC.md
if closed, err := sandbox.NetNS.Closed(); err != nil {
return nil, errors.Wrap(err, "failed to check network namespace closed")
return errors.Wrap(err, "failed to check network namespace closed")
} else if closed {
sandbox.NetNSPath = ""
}
if err := c.teardownPodNetwork(ctx, sandbox); err != nil {
return nil, errors.Wrapf(err, "failed to destroy network for sandbox %q", id)
return errors.Wrapf(err, "failed to destroy network for sandbox %q", id)
}
if err = sandbox.NetNS.Remove(); err != nil {
return nil, errors.Wrapf(err, "failed to remove network namespace for sandbox %q", id)
if err := sandbox.NetNS.Remove(); err != nil {
return errors.Wrapf(err, "failed to remove network namespace for sandbox %q", id)
}
}
log.G(ctx).Infof("TearDown network for sandbox %q successfully", id)
return &runtime.StopPodSandboxResponse{}, nil
return nil
}
// stopSandboxContainer kills the sandbox container.