Skip to content

Add timeout to container stop operations#121

Merged
carole-lavillonniere merged 5 commits intomainfrom
george/stop-timeout
Mar 17, 2026
Merged

Add timeout to container stop operations#121
carole-lavillonniere merged 5 commits intomainfrom
george/stop-timeout

Conversation

@gtsiolis
Copy link
Copy Markdown
Member

@gtsiolis gtsiolis commented Mar 17, 2026

Fix DES-172

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 6b4aeb74-ef59-44d1-b886-46244ea341bb

📥 Commits

Reviewing files that changed from the base of the PR and between a610317 and 3e14ff7.

📒 Files selected for processing (1)
  • internal/container/stop.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/container/stop.go

📝 Walkthrough

Walkthrough

Adds a 30s stopTimeout and wraps per-container operations in a 5s context.WithTimeout in internal/container/stop.go; applies the timeout to IsRunning and Stop, ensures cancel() is called on all paths, and preserves spinner and error behaviors.

Changes

Cohort / File(s) Summary
Container stop flow
internal/container/stop.go
Introduce stopTimeout (30s) and time import; use a per-container context.WithTimeout (5s) for IsRunning and Stop; ensure cancel() is invoked on all return paths; emit spinner start/stop and success messages within the timeout-limited flow.
Project metadata
manifest_file, go.mod
Minor project/module metadata changes (net +11 / -2 lines).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Add stop command #21: Original Stop implementation in internal/container/stop.go that this change wraps with per-container timeouts and spinner handling.

Suggested reviewers

  • carole-lavillonniere
  • silv-io
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a timeout mechanism to container stop operations, which aligns with the changeset.
Description check ✅ Passed The description references a specific issue (DES-172) and relates to the changeset's primary objective of adding timeout to container stop operations.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch george/stop-timeout
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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.

🧹 Nitpick comments (1)
internal/container/stop.go (1)

19-27: Timeout is shared across all containers in the loop.

The 10-second timeout applies to the entire function, not per container. If containers has multiple entries, the timeout budget is consumed by earlier iterations, leaving progressively less time for subsequent containers. This could lead to inconsistent behavior where the first container stops successfully but later containers timeout.

Consider either applying timeout per-container or scaling the timeout based on len(containers).

♻️ Option: Per-container timeout
 func Stop(ctx context.Context, rt runtime.Runtime, sink output.Sink, containers []config.ContainerConfig) error {
-	ctx, cancel := context.WithTimeout(ctx, stopTimeout)
-	defer cancel()
-
 	for _, c := range containers {
+		stopCtx, cancel := context.WithTimeout(ctx, stopTimeout)
 		name := c.Name()
-		running, err := rt.IsRunning(ctx, name)
+		running, err := rt.IsRunning(stopCtx, name)
 		if err != nil {
+			cancel()
 			return fmt.Errorf("checking %s running: %w", name, err)
 		}
 		if !running {
+			cancel()
 			return fmt.Errorf("LocalStack is not running")
 		}
 		output.EmitSpinnerStart(sink, "Stopping LocalStack...")
-		if err := rt.Stop(ctx, name); err != nil {
+		if err := rt.Stop(stopCtx, name); err != nil {
 			output.EmitSpinnerStop(sink)
+			cancel()
 			return fmt.Errorf("failed to stop LocalStack: %w", name, err)
 		}
 		output.EmitSpinnerStop(sink)
 		output.EmitSuccess(sink, "LocalStack stopped")
+		cancel()
 	}
 
 	return nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/container/stop.go` around lines 19 - 27, The timeout is currently
shared across the entire loop so earlier iterations can consume the 10s budget;
change to apply a per-container timeout by creating a new context with timeout
inside the loop (and defer/call the cancel) before calling rt.IsRunning, or
alternatively compute a scaled timeout like 10s * len(containers) and use that
when creating the ctx; update the loop in stop.go where containers is iterated
and rt.IsRunning(ctx, name) is invoked so each IsRunning call has its own
timeout-bound context (ensure you call the cancel to avoid leaks).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/container/stop.go`:
- Around line 19-27: The timeout is currently shared across the entire loop so
earlier iterations can consume the 10s budget; change to apply a per-container
timeout by creating a new context with timeout inside the loop (and defer/call
the cancel) before calling rt.IsRunning, or alternatively compute a scaled
timeout like 10s * len(containers) and use that when creating the ctx; update
the loop in stop.go where containers is iterated and rt.IsRunning(ctx, name) is
invoked so each IsRunning call has its own timeout-bound context (ensure you
call the cancel to avoid leaks).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 528e4621-2c30-44c8-bc62-d02211ab0ba0

📥 Commits

Reviewing files that changed from the base of the PR and between aa05700 and 68b082b.

📒 Files selected for processing (1)
  • internal/container/stop.go

@gtsiolis gtsiolis force-pushed the george/stop-timeout branch from 68b082b to 3fca32a Compare March 17, 2026 09:30
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

🧹 Nitpick comments (1)
internal/container/stop.go (1)

13-13: Make stop timeout injectable from the command boundary

Line 13 hardcodes 10s in domain logic. Consider passing timeout into Stop(...) so cmd-layer configuration can control it per environment.

As per coding guidelines, "Do not call config.Get() from domain/business-logic packages; extract values at the command boundary (cmd/) and pass as explicit function arguments".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/container/stop.go` at line 13, The hardcoded stopTimeout constant
(stopTimeout = 10 * time.Second) embeds a config value in domain logic; change
the Stop function signature (e.g., Stop(ctx context.Context, timeout
time.Duration) or Stop(ctx, timeout time.Duration) on the relevant type) to
accept a timeout parameter instead of using stopTimeout, remove the
package-level const, update internal uses inside Stop to use the passed timeout
for cancellation/Timer logic, and update all callers to pass the desired timeout
from the command layer (cmd/) where configuration is read.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/container/stop.go`:
- Around line 19-20: The shared context created with
containerCtx/containerCancel using stopTimeout causes IsRunning and the
subsequent Stop call (which invokes ContainerStop and ContainerRemove) to
compete for the same deadline and can make ContainerRemove falsely time out; fix
this by: 1) changing the function signature to accept an explicit timeout
parameter instead of using the package-level stopTimeout, 2) create and use
separate contexts for the pre-check (IsRunning) and for the actual stop/remove
operations (e.g., create a short-lived ctx for IsRunning and a fresh ctx for
Stop/ContainerStop/ContainerRemove) so the stop/remove gets a full timeout, and
3) ensure the timeout value is extracted at the command boundary and passed down
into this function.

---

Nitpick comments:
In `@internal/container/stop.go`:
- Line 13: The hardcoded stopTimeout constant (stopTimeout = 10 * time.Second)
embeds a config value in domain logic; change the Stop function signature (e.g.,
Stop(ctx context.Context, timeout time.Duration) or Stop(ctx, timeout
time.Duration) on the relevant type) to accept a timeout parameter instead of
using stopTimeout, remove the package-level const, update internal uses inside
Stop to use the passed timeout for cancellation/Timer logic, and update all
callers to pass the desired timeout from the command layer (cmd/) where
configuration is read.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 4ab01c40-e54c-4bf6-bcef-dd529a181bf5

📥 Commits

Reviewing files that changed from the base of the PR and between 68b082b and 3fca32a.

📒 Files selected for processing (1)
  • internal/container/stop.go

@gtsiolis gtsiolis force-pushed the george/stop-timeout branch from e4693e7 to a610317 Compare March 17, 2026 15:39
@gtsiolis
Copy link
Copy Markdown
Member Author

Thanks, @carole-lavillonniere! Tried to address all three comments in a610317.

Could you take another look when you're free?

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/container/stop.go`:
- Around line 19-31: The defer calls checkCancel() and stopCancel() inside the
loop cause context cancellation to be delayed until function exit; replace those
defers by calling checkCancel() immediately after the IsRunning(checkCtx, name)
result is handled and call stopCancel() immediately after the rt.Stop(stopCtx,
name) call completes (or on its error path) so each iteration's contexts are
cancelled before the next iteration; update the loop around IsRunning and Stop
to explicitly invoke checkCancel() and stopCancel() in all branches (success and
error) and keep IsRunning/Stop usage unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 17b7699f-c130-44d2-8a0f-c2176fff5afe

📥 Commits

Reviewing files that changed from the base of the PR and between 3fca32a and a610317.

📒 Files selected for processing (1)
  • internal/container/stop.go

@carole-lavillonniere carole-lavillonniere merged commit f28e1c4 into main Mar 17, 2026
8 checks passed
@carole-lavillonniere carole-lavillonniere deleted the george/stop-timeout branch March 17, 2026 16:09
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