Repository: containerd/containerd
File: internal/cri/server/podsandbox/sandbox_stop.go (Go)

Code before:
	eventtypes "github.com/containerd/containerd/api/events"
	"github.com/containerd/containerd/v2/core/sandbox"
	"github.com/containerd/containerd/v2/internal/cri/server/podsandbox/types"
	sandboxstore "github.com/containerd/containerd/v2/internal/cri/store/sandbox"
	ctrdutil "github.com/containerd/containerd/v2/internal/cri/util"
	"github.com/containerd/containerd/v2/pkg/protobuf"
)

func (c *Controller) Stop(ctx context.Context, sandboxID string, _ ...sandbox.StopOpt) error {
	podSandbox := c.store.Get(sandboxID)
	if podSandbox == nil {
		return errdefs.ErrNotFound
	}
	if podSandbox.Container == nil {
		return nil
	}
	meta, err := getMetadata(ctx, podSandbox.Container)
	if err != nil {
		return err
	}
	state := podSandbox.Status.Get().State
	if state == sandboxstore.StateReady || state == sandboxstore.StateUnknown {
		if err := c.stopSandboxContainer(ctx, podSandbox); err != nil {
			return fmt.Errorf("failed to stop sandbox container %q in %q state: %w", sandboxID, state, err)
		}
	}
	if err := c.cleanupSandboxFiles(sandboxID, meta.Config); err != nil {
		return fmt.Errorf("failed to cleanup sandbox files: %w", err)
	}
	return nil
}

// stopSandboxContainer kills the sandbox container.
// `task.Delete` is not called here because it will be called when
// the event monitor handles the `TaskExit` event.
func (c *Controller) stopSandboxContainer(ctx context.Context, podSandbox *types.PodSandbox) error {
	id := podSandbox.ID
	container := podSandbox.Container
	state := podSandbox.Status.Get().State
	task, err := container.Task(ctx, nil)
	if err != nil {
		if !errdefs.IsNotFound(err) {
			return fmt.Errorf("failed to get pod sandbox container: %w", err)
		}
		// Don't return for unknown state, some cleanup needs to be done.
		if state == sandboxstore.StateUnknown {
			return cleanupUnknownSandbox(ctx, id, podSandbox)
		}
		return nil
	}


Review comment (suggestion):
In e2e test, the task will be removed after delete. so in e2e env, it's unlikely to hit this error again during retry. but reality is that it could happen again. Left comment here just in case that we need to revisit it.

https://github.com/containerd/containerd/blob/0034ae955d6befb86026c7126bba0e1f3af4fa09/core/runtime/v2/shim.go#L539-L557

Code after:
	eventtypes "github.com/containerd/containerd/api/events"
	"github.com/containerd/containerd/v2/core/sandbox"
	"github.com/containerd/containerd/v2/internal/cri/server/podsandbox/types"
	sandboxstore "github.com/containerd/containerd/v2/internal/cri/store/sandbox"
	ctrdutil "github.com/containerd/containerd/v2/internal/cri/util"
	"github.com/containerd/containerd/v2/pkg/protobuf"
)

func (c *Controller) Stop(ctx context.Context, sandboxID string, _ ...sandbox.StopOpt) error {
	podSandbox := c.store.Get(sandboxID)
	if podSandbox == nil {
		return errdefs.ErrNotFound
	}
	if podSandbox.Container == nil {
		return nil
	}
	meta, err := getMetadata(ctx, podSandbox.Container)
	if err != nil {
		return err
	}
	state := podSandbox.Status.Get().State
	if state == sandboxstore.StateReady || state == sandboxstore.StateUnknown {
		err = c.stopSandboxContainer(ctx, podSandbox)
		if err != nil && isShimTTRPCClosed(err) {
			log.G(ctx).WithError(err).
				Warnf("Shim ttrpc connection closed when stopping sandbox container %q in %q state, retrying", sandboxID, state)

			err = c.stopSandboxContainer(ctx, podSandbox)
		}
		if err != nil {
			return fmt.Errorf("failed to stop sandbox container %q in %q state: %w", sandboxID, state, err)
		}
	}
	if err := c.cleanupSandboxFiles(sandboxID, meta.Config); err != nil {
		return fmt.Errorf("failed to cleanup sandbox files: %w", err)
	}
	return nil
}

// stopSandboxContainer kills the sandbox container.
// `task.Delete` is not called here because it will be called when
// the event monitor handles the `TaskExit` event.
func (c *Controller) stopSandboxContainer(ctx context.Context, podSandbox *types.PodSandbox) error {
	id := podSandbox.ID
	container := podSandbox.Container
	state := podSandbox.Status.Get().State
	task, err := container.Task(ctx, nil)
	if err != nil {
		if !errdefs.IsNotFound(err) {
			return fmt.Errorf("failed to get pod sandbox container: %w", err)
		}