stopContainer stops a container based on the container metadata.
(ctx context.Context, container containerstore.Container, timeout time.Duration)
| 109 | |
| 110 | // stopContainer stops a container based on the container metadata. |
| 111 | func (c *criService) stopContainer(ctx context.Context, container containerstore.Container, timeout time.Duration) error { |
| 112 | span := tracing.SpanFromContext(ctx) |
| 113 | start := time.Now() |
| 114 | id := container.ID |
| 115 | sandboxID := container.SandboxID |
| 116 | |
| 117 | // Return without error if container is not running. This makes sure that |
| 118 | // stop only takes real action after the container is started. |
| 119 | state := container.Status.Get().State() |
| 120 | if state != runtime.ContainerState_CONTAINER_RUNNING && |
| 121 | state != runtime.ContainerState_CONTAINER_UNKNOWN { |
| 122 | log.G(ctx).Infof("Container to stop %q must be in running or unknown state, current state %q", |
| 123 | id, criContainerStateToString(state)) |
| 124 | return nil |
| 125 | } |
| 126 | |
| 127 | task, err := container.Container.Task(ctx, nil) |
| 128 | if err != nil { |
| 129 | if !errdefs.IsNotFound(err) { |
| 130 | return fmt.Errorf("failed to get task for container %q: %w", id, err) |
| 131 | } |
| 132 | // Don't return for unknown state, some cleanup needs to be done. |
| 133 | if state == runtime.ContainerState_CONTAINER_UNKNOWN { |
| 134 | return c.cleanupUnknownContainer(ctx, id, container, sandboxID) |
| 135 | } |
| 136 | return nil |
| 137 | } |
| 138 | |
| 139 | // Handle unknown state. |
| 140 | if state == runtime.ContainerState_CONTAINER_UNKNOWN { |
| 141 | // Start an exit handler for containers in unknown state. |
| 142 | waitCtx, waitCancel := context.WithCancel(ctrdutil.NamespacedContext()) |
| 143 | defer waitCancel() |
| 144 | exitCh, err := task.Wait(waitCtx) |
| 145 | if err != nil { |
| 146 | if !errdefs.IsNotFound(err) { |
| 147 | return fmt.Errorf("failed to wait for task for %q: %w", id, err) |
| 148 | } |
| 149 | return c.cleanupUnknownContainer(ctx, id, container, sandboxID) |
| 150 | } |
| 151 | |
| 152 | exitCtx, exitCancel := context.WithCancel(context.Background()) |
| 153 | stopCh := c.startContainerExitMonitor(exitCtx, id, task.Pid(), exitCh) |
| 154 | defer func() { |
| 155 | exitCancel() |
| 156 | // This ensures that exit monitor is stopped before |
| 157 | // `Wait` is cancelled, so no exit event is generated |
| 158 | // because of the `Wait` cancellation. |
| 159 | <-stopCh |
| 160 | }() |
| 161 | } |
| 162 | |
| 163 | // We only need to kill the task. The event handler will Delete the |
| 164 | // task from containerd after it handles the Exited event. |
| 165 | if timeout > 0 { |
| 166 | stopSignal := "SIGTERM" |
| 167 | if signal := container.Config.GetStopSignal(); signal != runtime.Signal_RUNTIME_DEFAULT { |
| 168 | stopSignal, err = criSignalToOCIStopSignal(signal) |
no test coverage detected