uniform context usage in commands; some unused parameters removed#35
uniform context usage in commands; some unused parameters removed#35skevetter merged 2 commits intoskevetter:mainfrom
Conversation
📝 WalkthroughWalkthroughRemoved Changes
Sequence Diagram(s)sequenceDiagram
participant User as User / Cobra CLI
participant Cobra as Cobra Command
participant Cmd as Command.Run
participant SSH as ssh.Run
participant GCloud as gcloud.Client
rect rgba(0,128,0,0.5)
User->>Cobra: invoke command
Cobra->>Cmd: call Run(cobraCmd.Context(), optionsFromEnv)
Cmd->>SSH: ssh.Run(ctx, RunOptions{...})
SSH->>GCloud: use ctx to create/use client
GCloud-->>SSH: return instance data
SSH-->>Cmd: return results
Cmd-->>Cobra: render output
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
d6ff72b to
97f12c8
Compare
97f12c8 to
144efa6
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pkg/gcloud/gcloud.go (1)
215-225:⚠️ Potential issue | 🟠 MajorHandle
SUSPENDEDas a valid VM state.
compute.instances.getcan returnSUSPENDED. Right now that falls through tounexpected status, sostatusfails for a legitimate instance state instead of reporting a provider status. Mapping it toclient.StatusStoppedwould keep the command usable.Suggested fix
switch status { case "RUNNING": return client.StatusRunning, nil case "STOPPING", "SUSPENDING", "REPAIRING", "PROVISIONING", "STAGING": return client.StatusBusy, nil - case "TERMINATED": + case "TERMINATED", "SUSPENDED": return client.StatusStopped, nil }What are the documented Google Compute Engine VM instance status values, and does that list include SUSPENDED?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/gcloud/gcloud.go` around lines 215 - 225, The switch on the normalized GCE instance status currently treats "SUSPENDED" as unexpected; update the mapping in the status-mapping block (the code that sets status := strings.TrimSpace(strings.ToUpper(*instance.Status)) and switches on status) to include "SUSPENDED" as a recognized VM state and return client.StatusStopped for it (i.e., add "SUSPENDED" to the case that returns client.StatusStopped alongside "TERMINATED"). Keep the existing default fallback to return client.StatusNotFound and the error for any truly unexpected values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/gcloud/gcloud.go`:
- Around line 234-237: The json.MarshalIndent call failure is currently
swallowed and returns client.DescriptionNotFound with a nil error; change this
to return a non-nil error so callers don't treat it as a successful "not found"
result: when json.MarshalIndent(instance, "", " ") returns err, return a
non-empty error (e.g. return "", fmt.Errorf("failed to marshal instance: %w",
err)) instead of client.DescriptionNotFound, nil so that callers (like
cmd/describe.go) will detect the failure; ensure you import fmt if needed and
keep the json.MarshalIndent and instance symbols unchanged.
---
Outside diff comments:
In `@pkg/gcloud/gcloud.go`:
- Around line 215-225: The switch on the normalized GCE instance status
currently treats "SUSPENDED" as unexpected; update the mapping in the
status-mapping block (the code that sets status :=
strings.TrimSpace(strings.ToUpper(*instance.Status)) and switches on status) to
include "SUSPENDED" as a recognized VM state and return client.StatusStopped for
it (i.e., add "SUSPENDED" to the case that returns client.StatusStopped
alongside "TERMINATED"). Keep the existing default fallback to return
client.StatusNotFound and the error for any truly unexpected values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0686ce8c-d4c4-45d6-a238-79726952201f
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (11)
cmd/command.gocmd/create.gocmd/delete.gocmd/describe.gocmd/init.gocmd/start.gocmd/status.gocmd/stop.gocmd/token.gogo.modpkg/gcloud/gcloud.go
| bytes, err := json.MarshalIndent(instance, "", " ") | ||
| if err != nil { | ||
| return "{}", nil | ||
| return client.DescriptionNotFound, nil | ||
| } |
There was a problem hiding this comment.
Don't turn marshal failures into a successful DescriptionNotFound response.
Line 236 drops the serialization error and returns success. Since cmd/describe.go prints the returned string when err == nil, this can report "not found" for an existing instance and exit 0.
Suggested fix
bytes, err := json.MarshalIndent(instance, "", " ")
if err != nil {
- return client.DescriptionNotFound, nil
+ return client.DescriptionNotFound, err
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| bytes, err := json.MarshalIndent(instance, "", " ") | |
| if err != nil { | |
| return "{}", nil | |
| return client.DescriptionNotFound, nil | |
| } | |
| bytes, err := json.MarshalIndent(instance, "", " ") | |
| if err != nil { | |
| return client.DescriptionNotFound, err | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/gcloud/gcloud.go` around lines 234 - 237, The json.MarshalIndent call
failure is currently swallowed and returns client.DescriptionNotFound with a nil
error; change this to return a non-nil error so callers don't treat it as a
successful "not found" result: when json.MarshalIndent(instance, "", " ")
returns err, return a non-empty error (e.g. return "", fmt.Errorf("failed to
marshal instance: %w", err)) instead of client.DescriptionNotFound, nil so that
callers (like cmd/describe.go) will detect the failure; ensure you import fmt if
needed and keep the json.MarshalIndent and instance symbols unchanged.
d2e4fb4 to
646d41f
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
cmd/stop.go (1)
83-90:⚠️ Potential issue | 🟠 MajorBug: Error is swallowed when reading error response body succeeds.
Line 86 wraps
err(which isnilafter a successfulio.ReadAll) instead of creating a new error. When the status code indicates failure (≥400) but the body is read successfully, this returnsnil, silently ignoring the HTTP error.🐛 Proposed fix
if resp.StatusCode >= 400 { out, err := io.ReadAll(resp.Body) - if err == nil { - return errors.Wrapf(err, "Error stopping vm: %s", string(out)) + if err != nil { + return errors.Wrap(err, "reading error response body") } - - return err + return fmt.Errorf("error stopping vm: %s", string(out)) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/stop.go` around lines 83 - 90, The code handling HTTP error responses misuses the local err variable: when io.ReadAll(resp.Body) succeeds err is nil but the code calls errors.Wrapf(err, ...) and ends up returning nil, swallowing the HTTP error; update the error path in the block that checks resp.StatusCode >= 400 (using resp and out variables shown in the diff) so that when ReadAll succeeds you create and return a new error (e.g., errors.Errorf or errors.New with a descriptive message including the response body string/out and status) instead of wrapping the nil err, and keep returning the original err when ReadAll fails.cmd/command.go (1)
40-105:⚠️ Potential issue | 🟠 MajorPropagate tunnel startup cancellation instead of falling through.
The context plumbing stops short in the IAP branch: Line 86 still launches
gcloudwithexec.Command, and line 105 ignores whetherwaitForPortexited due to timeout or caller cancellation. If the context times out or is canceled,waitForPortreturns silently and execution continues to SSH connection setup against a tunnel that may never be ready.Proposed fix
- gcloudIAPcommand := exec.Command("gcloud", []string{ + gcloudIAPcommand := exec.CommandContext(ctx, "gcloud", []string{ "compute", "start-iap-tunnel", *instance.Name, "22", "--local-host-port=localhost:" + port, "--zone=" + *instance.Zone, }...) // open tunnel in background if err = gcloudIAPcommand.Start(); err != nil { return fmt.Errorf("start tunnel: %w", err) } defer func() { err = gcloudIAPcommand.Process.Kill() }() timeoutCtx, cancelFn := context.WithTimeout(ctx, 30*time.Second) defer cancelFn() - waitForPort(timeoutCtx, port) + if err := waitForPort(timeoutCtx, port); err != nil { + return fmt.Errorf("wait for IAP tunnel on port %s: %w", port, err) + } } sshClient, err := ssh.NewSSHClient("devpod", target+":"+port, privateKey) if err != nil { return errors.Wrap(err, "create ssh client") } defer func() { _ = sshClient.Close() }() // run command return ssh.Run(ctx, ssh.RunOptions{ Client: sshClient, Command: command, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, EnvVars: nil, }) } -func waitForPort(ctx context.Context, port string) { +func waitForPort(ctx context.Context, port string) error { for { select { case <-ctx.Done(): - return + return ctx.Err() default: l, err := net.Listen("tcp", "localhost:"+port) if err != nil { // port is taken - return + return nil } _ = l.Close() time.Sleep(1 * time.Second) } } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/command.go` around lines 40 - 105, In Run, the IAP tunnel branch must not ignore cancellation/timeouts from waitForPort: call waitForPort with timeoutCtx and check its returned error (or context.Done) and if it indicates timeout/cancellation return that error (or wrap it) before proceeding to SSH; ensure gcloudIAPcommand is terminated on that early return (use the existing deferred kill or call Process.Kill on error) and propagate the context error instead of continuing to SSH, referencing Run, gcloudIAPcommand, timeoutCtx, waitForPort and the IAP branch logic around findAvailablePort.
♻️ Duplicate comments (1)
pkg/gcloud/gcloud.go (1)
234-237:⚠️ Potential issue | 🟠 MajorDon't turn marshal failures into a successful
DescriptionNotFoundresponse.Line 236 returns
nilfor the error whenjson.MarshalIndentfails. This silently swallows the serialization error and causescmd/describe.goto print"not found"for an existing instance.🐛 Proposed fix
bytes, err := json.MarshalIndent(instance, "", " ") if err != nil { - return client.DescriptionNotFound, nil + return client.DescriptionNotFound, err }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/gcloud/gcloud.go` around lines 234 - 237, The code is swallowing json.MarshalIndent failures by returning client.DescriptionNotFound with a nil error; instead, when json.MarshalIndent(instance, ...) returns an error you should return that error (or wrap it with context) so callers know serialization failed rather than pretending the instance was "not found"; locate the marshal call (json.MarshalIndent and the variable instance) and change the return to propagate the marshal error (e.g., return client.DescriptionNotFound, fmt.Errorf("marshal instance: %w", err) or simply return nil, err depending on the surrounding function signature) rather than returning nil error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@cmd/command.go`:
- Around line 40-105: In Run, the IAP tunnel branch must not ignore
cancellation/timeouts from waitForPort: call waitForPort with timeoutCtx and
check its returned error (or context.Done) and if it indicates
timeout/cancellation return that error (or wrap it) before proceeding to SSH;
ensure gcloudIAPcommand is terminated on that early return (use the existing
deferred kill or call Process.Kill on error) and propagate the context error
instead of continuing to SSH, referencing Run, gcloudIAPcommand, timeoutCtx,
waitForPort and the IAP branch logic around findAvailablePort.
In `@cmd/stop.go`:
- Around line 83-90: The code handling HTTP error responses misuses the local
err variable: when io.ReadAll(resp.Body) succeeds err is nil but the code calls
errors.Wrapf(err, ...) and ends up returning nil, swallowing the HTTP error;
update the error path in the block that checks resp.StatusCode >= 400 (using
resp and out variables shown in the diff) so that when ReadAll succeeds you
create and return a new error (e.g., errors.Errorf or errors.New with a
descriptive message including the response body string/out and status) instead
of wrapping the nil err, and keep returning the original err when ReadAll fails.
---
Duplicate comments:
In `@pkg/gcloud/gcloud.go`:
- Around line 234-237: The code is swallowing json.MarshalIndent failures by
returning client.DescriptionNotFound with a nil error; instead, when
json.MarshalIndent(instance, ...) returns an error you should return that error
(or wrap it with context) so callers know serialization failed rather than
pretending the instance was "not found"; locate the marshal call
(json.MarshalIndent and the variable instance) and change the return to
propagate the marshal error (e.g., return client.DescriptionNotFound,
fmt.Errorf("marshal instance: %w", err) or simply return nil, err depending on
the surrounding function signature) rather than returning nil error.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 09582cd4-f0d3-47a8-865e-5712f9d56f3e
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (11)
cmd/command.gocmd/create.gocmd/delete.gocmd/describe.gocmd/init.gocmd/start.gocmd/status.gocmd/stop.gocmd/token.gogo.modpkg/gcloud/gcloud.go
🚧 Files skipped from review as they are similar to previous changes (2)
- go.mod
- cmd/token.go
- chore: track changes to DevPod - chore: use cobra.Command.Context uniformly - chore(cli): remove some spurious types and parameters
646d41f to
6d15a5e
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
pkg/gcloud/gcloud.go (1)
234-236:⚠️ Potential issue | 🟠 MajorDon't swallow
json.MarshalIndentfailures as a successful not-found result.Line 236 still returns
nilfor the error path, so callers likecmd/describe.gowill print a not-found sentinel and exit successfully even when serialization failed for an existing instance.Suggested fix
bytes, err := json.MarshalIndent(instance, "", " ") if err != nil { - return client.DescriptionNotFound, nil + return "", fmt.Errorf("failed to marshal instance description: %w", err) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/gcloud/gcloud.go` around lines 234 - 236, The json.MarshalIndent error path currently returns client.DescriptionNotFound with a nil error, swallowing the actual marshal failure; change the error handling so that when json.MarshalIndent(instance, "", " ") returns err you return a non-nil error (e.g., return nil, err) instead of client.DescriptionNotFound, nil so callers like cmd/describe.go receive and surface the serialization error; update the error return to propagate err from the json.MarshalIndent call (referencing json.MarshalIndent, instance, and client.DescriptionNotFound).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/token.go`:
- Around line 20-21: The root command is being executed without a context so
cobraCmd.Context() in handlers like the RunE in cmd/token.go returns nil; fix
this by invoking the root command with a background context (e.g. replace
Execute() calls in main.go and where rootCmd is run with
ExecuteContext(context.Background())) so that cobraCmd.Context() is non-nil for
handlers (alternatively call rootCmd.SetContext(context.Background()) before
Execute()); update main.go and the root command invocation accordingly so RunE
handlers (e.g. the function in cmd/token.go) receive a valid context.
---
Duplicate comments:
In `@pkg/gcloud/gcloud.go`:
- Around line 234-236: The json.MarshalIndent error path currently returns
client.DescriptionNotFound with a nil error, swallowing the actual marshal
failure; change the error handling so that when json.MarshalIndent(instance, "",
" ") returns err you return a non-nil error (e.g., return nil, err) instead of
client.DescriptionNotFound, nil so callers like cmd/describe.go receive and
surface the serialization error; update the error return to propagate err from
the json.MarshalIndent call (referencing json.MarshalIndent, instance, and
client.DescriptionNotFound).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e2832bd7-863e-4a54-83ab-7fdaf22404ac
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (11)
cmd/command.gocmd/create.gocmd/delete.gocmd/describe.gocmd/init.gocmd/start.gocmd/status.gocmd/stop.gocmd/token.gogo.modpkg/gcloud/gcloud.go
🚧 Files skipped from review as they are similar to previous changes (3)
- cmd/describe.go
- cmd/stop.go
- cmd/create.go
| RunE: func(cobraCmd *cobra.Command, args []string) error { | ||
| return cmd.Run(cobraCmd.Context()) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Execute / ExecuteContext call sites:\n'
rg -n -C2 --type go '\.(ExecuteContext|Execute)\('
printf '\nSetContext call sites:\n'
rg -n -C2 --type go '\.SetContext\('Repository: skevetter/devpod-provider-gcloud
Length of output: 397
Fix the root command to provide a context before executing these subcommands.
The root command is invoked with plain Execute() in main.go:8 and cmd/root.go:36, which means cobraCmd.Context() will be nil when these refactored command handlers run. The new code at cmd/token.go:20-21 and similar locations passes this nil context downstream to gcloud/oauth helpers.
Use one of:
- Change to
cmd.ExecuteContext(context.Background())inmain.go:8androotCmd.ExecuteContext(context.Background())incmd/root.go:36 - Call
rootCmd.SetContext(context.Background())beforeExecute() - Add a fallback in each command handler:
ctx := cobraCmd.Context(); if ctx == nil { ctx = context.Background() }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cmd/token.go` around lines 20 - 21, The root command is being executed
without a context so cobraCmd.Context() in handlers like the RunE in
cmd/token.go returns nil; fix this by invoking the root command with a
background context (e.g. replace Execute() calls in main.go and where rootCmd is
run with ExecuteContext(context.Background())) so that cobraCmd.Context() is
non-nil for handlers (alternatively call
rootCmd.SetContext(context.Background()) before Execute()); update main.go and
the root command invocation accordingly so RunE handlers (e.g. the function in
cmd/token.go) receive a valid context.
Summary by CodeRabbit
Chores
Bug Fixes / Improvements