Skip to content

Add stop command#21

Merged
carole-lavillonniere merged 8 commits intomainfrom
george/des-108
Feb 13, 2026
Merged

Add stop command#21
carole-lavillonniere merged 8 commits intomainfrom
george/des-108

Conversation

@gtsiolis
Copy link
Copy Markdown
Member

@gtsiolis gtsiolis commented Feb 11, 2026

Fix DES-108

Screenshot 2026-02-11 at 17 43 13 (2)

msg := e.Err.Error()

if strings.Contains(msg, "No such container") || strings.Contains(msg, "not found") {
return fmt.Sprintf("%s is not running", e.Name)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: It seems like the docker sdk is defining functions that can be used to check the error instead of doing string comparison: https://pkg.go.dev/github.com/containerd/errdefs#IsNotFound

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in 1f8e3eb.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this change I don't think there is a need to define a custom error StopError anymore

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! Changed in a55d50a.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

Adds a new CLI subcommand "stop" and the runtime/container support to stop and remove configured containers, including Docker runtime implementations and integration tests. (47 words)

Changes

Cohort / File(s) Summary
CLI Command
cmd/stop.go
Adds stopCmd command that builds a Docker runtime, wires an onProgress callback, and calls container.Stop; exits on errors.
Container Orchestration
internal/container/.../start.go, internal/container/stop.go
Start now removes existing container before pulling; new container.Stop(ctx, rt, onProgress) iterates configured containers, reports progress, and calls runtime.Stop for each.
Runtime Abstraction & Impl
internal/runtime/runtime.go, internal/runtime/docker.go
Runtime interface extended with Stop and Remove; DockerRuntime implements Stop (ContainerStop + ContainerRemove) and Remove.
Dependency / Modules
go.mod
Promotes github.com/containerd/errdefs from indirect to direct dependency.
Integration Tests
test/integration/stop_test.go
Adds integration tests covering successful stop, failure when container not running, and idempotency; includes helper to start test container.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as CLI Command\n(cmd/stop.go)
    participant Orch as Container Orchestration\n(internal/container/stop.go)
    participant Runtime as Docker Runtime\n(internal/runtime/docker.go)
    participant Docker as Docker Daemon

    User->>CLI: run `stop` command
    CLI->>CLI: create DockerRuntime
    CLI->>Orch: Stop(ctx, rt, onProgress)
    Orch->>Orch: load config, iterate containers
    loop per container
        Orch->>Orch: onProgress("Stopping <name>")
        Orch->>Runtime: Stop(ctx, <name>)
        Runtime->>Docker: ContainerStop(<name>)
        Docker-->>Runtime: stopped
        Runtime->>Docker: ContainerRemove(<name>)
        Docker-->>Runtime: removed
        Orch->>Orch: onProgress("stopped <name>")
    end
    Orch-->>CLI: return result
    CLI-->>User: print progress/status
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

🐰 I hopped to the CLI and gave a small knock,
Told Docker to hush and to close up the dock,
With Stop and Remove I tidy the ground,
A soft little hop — containers safe and sound.

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add stop command' directly and clearly summarizes the main change: adding a new stop subcommand to the CLI.
Description check ✅ Passed The description references Fix DES-108 with a screenshot demonstrating the stop command execution, which is directly related to the changeset.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch george/des-108

Comment @coderabbitai help to get the list of available commands and usage tips.

@gtsiolis
Copy link
Copy Markdown
Member Author

Some of the checks also fail in main. 👀

@carole-lavillonniere, could you take another look when you get a chance? 😁

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/integration/stop_test.go`:
- Around line 14-22: The test fails when the image is missing because
startTestContainer calls dockerClient.ContainerCreate without pulling the image;
update startTestContainer to explicitly pull "alpine:latest" before calling
dockerClient.ContainerCreate by invoking dockerClient.ImagePull(ctx,
"alpine:latest", types.ImagePullOptions{}), reading and closing the returned
io.ReadCloser to completion, checking for errors, then proceed with
dockerClient.ContainerCreate and dockerClient.ContainerStart (use the same
containerName/resp.ID as before).

@carole-lavillonniere
Copy link
Copy Markdown
Collaborator

Some of the checks also fail in main. 👀

@carole-lavillonniere, could you take another look when you get a chance? 😁

We are aware of the integration tests failing on windows/main, just created an issue: DRG-505

However there are more tests failing in this PR, namely:

=== RUN   TestStopCommandSucceeds
    stop_test.go:33: 
        	Error Trace:	/home/runner/work/lstk/lstk/test/integration/stop_test.go:20
        	            				/home/runner/work/lstk/lstk/test/integration/stop_test.go:33
        	Error:      	Received unexpected error:
        	            	Error response from daemon: No such image: alpine:latest
        	Test:       	TestStopCommandSucceeds
        	Messages:   	failed to create test container
--- FAIL: TestStopCommandSucceeds (0.00s)

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@internal/container/start.go`:
- Around line 53-57: The error check in the container removal loop uses
containerd's errdefs, causing Docker-specific errors to be misclassified; update
the import and check to use Docker's errdefs: replace usages of
containerd/errdefs.IsNotFound(err) with
github.com/docker/docker/errdefs.IsNotFound(err) (the removal loop calling
rt.Remove and the DockerRuntime.Remove implementation will then correctly allow
not-found errors to be ignored).

In `@internal/runtime/docker.go`:
- Around line 99-107: The Stop implementation currently calls ContainerRemove
with empty RemoveOptions which leaves volumes behind; update DockerRuntime.Stop
to call ContainerRemove(ctx, containerName,
container.RemoveOptions{RemoveVolumes: true}) so volumes are removed when
stopping a container (and consider making the same change for
DockerRuntime.Remove if removal should always include volumes), or alternatively
add a comment/docstring on DockerRuntime.Stop and DockerRuntime.Remove
clarifying that volumes are intentionally retained.
🧹 Nitpick comments (1)
cmd/stop.go (1)

12-31: Use RunE to return errors instead of calling os.Exit directly.

This is the recommended Cobra pattern for commands that can fail. It improves testability and lets Cobra handle error output and exit codes consistently. Errors returned from RunE are processed at the top-level Execute() call, which handles the exit code once instead of scattered throughout the command logic.

♻️ Proposed refactor
 var stopCmd = &cobra.Command{
 	Use:   "stop",
 	Short: "Stop LocalStack",
 	Long:  "Stop the LocalStack emulator.",
-	Run: func(cmd *cobra.Command, args []string) {
+	RunE: func(cmd *cobra.Command, args []string) error {
 		rt, err := runtime.NewDockerRuntime()
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
-			os.Exit(1)
+			return err
 		}
 
 		onProgress := func(msg string) {
-			fmt.Println(msg)
+			fmt.Fprintln(cmd.OutOrStdout(), msg)
 		}
 
 		if err := container.Stop(cmd.Context(), rt, onProgress); err != nil {
-			fmt.Fprintln(os.Stderr, err)
-			os.Exit(1)
+			return err
 		}
+		return nil
 	},
 }

Comment on lines 53 to +57
for _, config := range containers {
// Remove any existing stopped container with the same name
if err := rt.Remove(ctx, config.Name); err != nil && !errdefs.IsNotFound(err) {
return fmt.Errorf("failed to remove existing container %s: %w", config.Name, err)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's understand the file structure and locate the relevant code
head -60 internal/container/start.go | cat -n

Repository: localstack/lstk

Length of output: 1912


🏁 Script executed:

# Check imports in the start.go file to understand what errdefs is being used
head -20 internal/container/start.go | cat -n

Repository: localstack/lstk

Length of output: 630


🏁 Script executed:

# Search for the rt.Remove definition and understand what rt is
rg -B 10 "rt.Remove" internal/container/start.go

Repository: localstack/lstk

Length of output: 364


🏁 Script executed:

# Look for the runtime type definition in the codebase
rg "type.*Runtime" internal/container/ --max-count 5

Repository: localstack/lstk

Length of output: 41


🏁 Script executed:

# Check for Docker/containerd imports across the codebase
rg "containerd|docker" internal/container/start.go

Repository: localstack/lstk

Length of output: 91


🏁 Script executed:

# Find the runtime package and interface definition
find internal/runtime -type f -name "*.go" | head -10

Repository: localstack/lstk

Length of output: 113


🏁 Script executed:

# Look for the Runtime interface definition
rg "type Runtime interface" internal/runtime/

Repository: localstack/lstk

Length of output: 111


🏁 Script executed:

# Search for Remove method implementation
rg -A 10 "func.*Remove" internal/runtime/ --max-count 5

Repository: localstack/lstk

Length of output: 749


🏁 Script executed:

# Check what Docker client is being used in the runtime implementation
rg "docker|Docker" internal/runtime/ | head -20

Repository: localstack/lstk

Length of output: 1448


🏁 Script executed:

# Let's check the complete Remove method and error handling in docker.go
cat -n internal/runtime/docker.go | sed -n '/func (d \*DockerRuntime) Remove/,/^[0-9]*\tFunc/p' | head -20

Repository: localstack/lstk

Length of output: 840


🏁 Script executed:

# Check if there's any error handling or conversion in the docker.go file
cat internal/runtime/docker.go | head -50

Repository: localstack/lstk

Length of output: 1236


🏁 Script executed:

# Look for any other error handling in docker.go to see patterns
rg "errdefs\|error" internal/runtime/docker.go

Repository: localstack/lstk

Length of output: 41


🏁 Script executed:

# Check the complete docker.go file size and content
wc -l internal/runtime/docker.go && echo "---" && cat internal/runtime/docker.go

Repository: localstack/lstk

Length of output: 3568


Replace containerd/errdefs.IsNotFound() with docker/errdefs.IsNotFound() for Docker error compatibility.

The Remove() method in DockerRuntime returns errors directly from the Docker client's ContainerRemove(), which returns Docker-specific error types from the docker/errdefs package. However, the error check at line 55 uses containerd/errdefs.IsNotFound(), which cannot recognize Docker errors. This causes the code to fail when a container doesn't exist instead of gracefully skipping the removal, breaking fresh starts.

Import github.com/docker/docker/errdefs and use errdefs.IsNotFound(err) from the Docker package instead of the containerd version.

🤖 Prompt for AI Agents
In `@internal/container/start.go` around lines 53 - 57, The error check in the
container removal loop uses containerd's errdefs, causing Docker-specific errors
to be misclassified; update the import and check to use Docker's errdefs:
replace usages of containerd/errdefs.IsNotFound(err) with
github.com/docker/docker/errdefs.IsNotFound(err) (the removal loop calling
rt.Remove and the DockerRuntime.Remove implementation will then correctly allow
not-found errors to be ignored).

Comment on lines +99 to +107
func (d *DockerRuntime) Stop(ctx context.Context, containerName string) error {
if err := d.client.ContainerStop(ctx, containerName, container.StopOptions{}); err != nil {
return err
}
return d.client.ContainerRemove(ctx, containerName, container.RemoveOptions{})
}

func (d *DockerRuntime) Remove(ctx context.Context, containerName string) error {
return d.client.ContainerRemove(ctx, containerName, container.RemoveOptions{})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

github.com/docker/docker v28 ContainerStopOptions defaults timeout

💡 Result:

In github.com/docker/docker v28, ContainerStopOptions (API type container.StopOptions) has:

  • Timeout *int (seconds). If you don’t set it, it stays nil (so it’s omitted). [1]

What happens when Timeout == nil:

  • The client won’t send the t/timeout query parameter at all. [2]
  • The daemon then uses the container’s configured StopTimeout (if set); otherwise it falls back to the engine default. [3]

Engine default stop timeout (when neither API timeout nor per-container StopTimeout is set):

  • non-Windows: 10 seconds (defaultStopTimeout = 10) [4]
  • Windows: 30 seconds (defaultStopTimeout = 30) [5]

Sources
[1] (pkg.go.dev)
[2] (fuchsia.googlesource.com)
[3] (pkg.go.dev)
[4] (fossies.org)
[5] (fossies.org)

Citations:


🌐 Web query:

github.com/docker/docker v28 ContainerRemoveOptions defaults volume removal

💡 Result:

In github.com/docker/docker v28, ContainerRemoveOptions.RemoveVolumes is a bool with no explicit default set in code, so its zero-value default is false (i.e., volumes are not removed unless you set it). [1]

This matches the Docker Engine API: DELETE /containers/{id} has query param v (“remove volumes”) with default false. [2]

So, to remove volumes you must opt in, e.g.:

  • Go: ContainerRemoveOptions{RemoveVolumes: true}
  • API: DELETE .../containers/{id}?v=1
  • CLI: docker rm -v ... / --volumes [3]

Sources: [1] [2] [3]


Clarify whether volumes should be removed in the Stop operation.

The Stop() method uses empty StopOptions{}, which defers to the Docker daemon's default timeout (10 seconds on non-Windows, 30 seconds on Windows)—a reasonable default. However, ContainerRemove() with empty RemoveOptions{} leaves volumes behind by default (equivalent to docker rm without -v). If lstk stop is meant to fully clean up the container and its associated resources, set RemoveVolumes: true in the RemoveOptions:

container.RemoveOptions{RemoveVolumes: true}

Otherwise, document the volume retention behavior if it is intentional.

🤖 Prompt for AI Agents
In `@internal/runtime/docker.go` around lines 99 - 107, The Stop implementation
currently calls ContainerRemove with empty RemoveOptions which leaves volumes
behind; update DockerRuntime.Stop to call ContainerRemove(ctx, containerName,
container.RemoveOptions{RemoveVolumes: true}) so volumes are removed when
stopping a container (and consider making the same change for
DockerRuntime.Remove if removal should always include volumes), or alternatively
add a comment/docstring on DockerRuntime.Stop and DockerRuntime.Remove
clarifying that volumes are intentionally retained.

@gtsiolis gtsiolis self-assigned this Feb 12, 2026
@carole-lavillonniere carole-lavillonniere merged commit 3a1aeed into main Feb 13, 2026
7 of 10 checks passed
@carole-lavillonniere carole-lavillonniere deleted the george/des-108 branch February 13, 2026 14:10
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@internal/container/stop.go`:
- Around line 12-20: The Stop function accepts onProgress func(string) but calls
it unguarded, which can panic if callers pass nil; at the start of Stop
(function Stop(ctx context.Context, rt runtime.Runtime, onProgress
func(string))) set a default no-op when onProgress == nil (e.g., onProgress =
func(string) {}), then continue using onProgress throughout the function
(including the places around the "Stopping %s..." and the later progress calls)
so calls are safe even when nil was provided.
- Around line 18-26: The loop over cfg.Containers currently returns on the first
stop error which leaves later containers running; change it to attempt stopping
all containers and only return an error after the loop: introduce a variable
(e.g., firstErr error) before the loop, call rt.Stop(ctx, name) for each
container, if err != nil handle IsNotFound by setting firstErr if unset and
continue, otherwise set firstErr = fmt.Errorf("failed to stop %s: %w", name,
err) if unset and continue; after the loop, if firstErr != nil return firstErr,
otherwise proceed normally (keep onProgress calls and existing name := c.Name()
usage).
- Around line 21-24: The check using errdefs.IsNotFound(err) in the caller
(rt.Stop) is unreachable for Docker runtime errors because
internal/runtime/docker.go currently returns raw Docker (moby) errors; update
the Docker runtime implementation in internal/runtime/docker.go (the Docker
Stop() implementation) to translate Docker "not found" errors into
containerd-style errors by detecting Docker's not-found condition (e.g., via
moby client helpers or checking the error string/type returned by the Docker
client) and returning an errdefs.NotFound error (or wrapping the docker error
into errdefs.NotFound) so callers can reliably use errdefs.IsNotFound(err) on
rt.Stop results.

Comment on lines +12 to +20
func Stop(ctx context.Context, rt runtime.Runtime, onProgress func(string)) error {
cfg, err := config.Get()
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}

for _, c := range cfg.Containers {
name := c.Name()
onProgress(fmt.Sprintf("Stopping %s...", name))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against nil onProgress to avoid a panic.

If callers pass nil, the current implementation will panic at Line 20/Line 27. Consider defaulting to a no-op.

🔧 Suggested fix
 func Stop(ctx context.Context, rt runtime.Runtime, onProgress func(string)) error {
+	if onProgress == nil {
+		onProgress = func(string) {}
+	}
 	cfg, err := config.Get()

Also applies to: 27-30

🤖 Prompt for AI Agents
In `@internal/container/stop.go` around lines 12 - 20, The Stop function accepts
onProgress func(string) but calls it unguarded, which can panic if callers pass
nil; at the start of Stop (function Stop(ctx context.Context, rt
runtime.Runtime, onProgress func(string))) set a default no-op when onProgress
== nil (e.g., onProgress = func(string) {}), then continue using onProgress
throughout the function (including the places around the "Stopping %s..." and
the later progress calls) so calls are safe even when nil was provided.

Comment on lines +18 to +26
for _, c := range cfg.Containers {
name := c.Name()
onProgress(fmt.Sprintf("Stopping %s...", name))
if err := rt.Stop(ctx, name); err != nil {
if errdefs.IsNotFound(err) {
return fmt.Errorf("%s is not running", name)
}
return fmt.Errorf("failed to stop %s: %w", name, err)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Stopping halts on first error, leaving later containers running.

Because you return on the first failure, any remaining containers will never be stopped. Consider continuing the loop and returning an aggregated/first error after attempting all stops.

♻️ Suggested fix (continue + return first error)
-	for _, c := range cfg.Containers {
+	var firstErr error
+	for _, c := range cfg.Containers {
 		name := c.Name()
 		onProgress(fmt.Sprintf("Stopping %s...", name))
 		if err := rt.Stop(ctx, name); err != nil {
 			if errdefs.IsNotFound(err) {
-				return fmt.Errorf("%s is not running", name)
+				if firstErr == nil {
+					firstErr = fmt.Errorf("%s is not running", name)
+				}
+				continue
 			}
-			return fmt.Errorf("failed to stop %s: %w", name, err)
+			if firstErr == nil {
+				firstErr = fmt.Errorf("failed to stop %s: %w", name, err)
+			}
+			continue
 		}
 		onProgress(fmt.Sprintf("%s stopped", name))
 	}
 
-	return nil
+	return firstErr
🤖 Prompt for AI Agents
In `@internal/container/stop.go` around lines 18 - 26, The loop over
cfg.Containers currently returns on the first stop error which leaves later
containers running; change it to attempt stopping all containers and only return
an error after the loop: introduce a variable (e.g., firstErr error) before the
loop, call rt.Stop(ctx, name) for each container, if err != nil handle
IsNotFound by setting firstErr if unset and continue, otherwise set firstErr =
fmt.Errorf("failed to stop %s: %w", name, err) if unset and continue; after the
loop, if firstErr != nil return firstErr, otherwise proceed normally (keep
onProgress calls and existing name := c.Name() usage).

Comment on lines +21 to +24
if err := rt.Stop(ctx, name); err != nil {
if errdefs.IsNotFound(err) {
return fmt.Errorf("%s is not running", name)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect Docker runtime error mapping for Stop.
echo "=== Searching for Stop method implementation ==="
rg -n -C3 'func\s+\(.*\)\s+Stop\s*\(' internal/runtime -g '*.go'
echo ""
echo "=== Searching for errdefs usage patterns ==="
rg -n -C3 'errdefs\.|IsNotFound|NotFound|No such container' internal/runtime -g '*.go'

Repository: localstack/lstk

Length of output: 579


🏁 Script executed:

#!/bin/bash
echo "=== Checking if errdefs is used anywhere in the codebase ==="
rg -n 'errdefs\.' --type go | head -20

echo ""
echo "=== Checking imports in container/stop.go ==="
head -30 internal/container/stop.go

echo ""
echo "=== Looking for error wrapping/normalization patterns ==="
rg -n 'fmt\.Errorf|errors\.Is|errors\.As|errdefs' internal/container/stop.go

Repository: localstack/lstk

Length of output: 1357


🏁 Script executed:

#!/bin/bash
echo "=== Checking docker.go for client initialization ==="
head -50 internal/runtime/docker.go | grep -A5 -B5 'client'

echo ""
echo "=== Full relevant sections of docker.go ==="
sed -n '1,100p' internal/runtime/docker.go | tail -60

echo ""
echo "=== Checking go.mod for Docker and errdefs versions ==="
rg 'containerd|moby|docker' go.mod

Repository: localstack/lstk

Length of output: 2889


🏁 Script executed:

#!/bin/bash
echo "=== Check for Docker errdefs or error handling patterns ==="
rg -n 'docker.*errdefs|errdefs.*docker' --type go

echo ""
echo "=== Search for ContainerStop error handling ==="
rg -n -B5 -A5 'ContainerStop' internal/runtime/docker.go

echo ""
echo "=== Check for error type assertions or conversions ==="
rg -n 'errors\.Is|errors\.As|type.*error' internal/runtime/docker.go | head -15

echo ""
echo "=== Look for any Docker error normalization ==="
rg -n 'IsNotFound|NotFound|errs' internal/runtime/docker.go

Repository: localstack/lstk

Length of output: 608


🏁 Script executed:

#!/bin/bash
echo "=== Check Runtime interface definition ==="
rg -n -A10 'type Runtime interface' internal/runtime

echo ""
echo "=== Check for other Runtime implementations ==="
fd -e go internal/runtime | xargs grep -l 'func.*Runtime.*Start\|func.*Runtime.*Stop' 

echo ""
echo "=== Check test files to understand expected error handling ==="
fd -e go -path '*/test*' -o -path '*_test.go' internal/runtime | head -5

echo ""
echo "=== Verify if containerd runtime exists ==="
ls -la internal/runtime/

Repository: localstack/lstk

Length of output: 1721


🏁 Script executed:

#!/bin/bash
echo "=== Check Docker client import to understand error types ==="
rg -n 'import' internal/runtime/docker.go | head -20

echo ""
echo "=== Search for any error handling comments or TODOs ==="
rg -n 'TODO|FIXME|BUG|XXX' internal/runtime/

echo ""
echo "=== Check how errors from Docker API are typically handled in Go ==="
rg -n -B2 -A2 'client\.Container' internal/runtime/docker.go | grep -A2 -B2 'err'

Repository: localstack/lstk

Length of output: 1398


errdefs.IsNotFound will not match Docker runtime errors—the condition is unreachable.

internal/runtime/docker.go returns Docker client errors directly without wrapping. Since Docker (moby) and containerd have incompatible error types, errdefs.IsNotFound() from containerd will never recognize Docker "not found" errors. The Stop() method must normalize Docker errors to errdefs format, or use Docker-specific error checking instead.

🤖 Prompt for AI Agents
In `@internal/container/stop.go` around lines 21 - 24, The check using
errdefs.IsNotFound(err) in the caller (rt.Stop) is unreachable for Docker
runtime errors because internal/runtime/docker.go currently returns raw Docker
(moby) errors; update the Docker runtime implementation in
internal/runtime/docker.go (the Docker Stop() implementation) to translate
Docker "not found" errors into containerd-style errors by detecting Docker's
not-found condition (e.g., via moby client helpers or checking the error
string/type returned by the Docker client) and returning an errdefs.NotFound
error (or wrapping the docker error into errdefs.NotFound) so callers can
reliably use errdefs.IsNotFound(err) on rt.Stop results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants