feat(sdk): add Go SDK foundation and core clients (1/4)#9
Conversation
Parent brainstorm (01) defines the layered measurement approach for evaluating Agent Sandbox warm pooling on OpenShift. Child documents cover cluster setup (02), measurements (03), and results synthesis (04). Assisted-By: 🤖 Claude Code
Assisted-By: 🤖 Claude Code Signed-off-by: Roland Huß <rhuss@redhat.com>
Assisted-By: 🤖 Claude Code Signed-off-by: Roland Huß <rhuss@redhat.com>
Add the Go SDK module with proto bindings, domain types, internal converters, and all core API clients (sandbox, provider, service, exec, file, health, SSH, TCP, policy). Module: github.com/NVIDIA/OpenShell/sdk/go Part 1 of 4 for issue NVIDIA#2044. Assisted-By: 🤖 Claude Code Signed-off-by: Roland Huß <rhuss@redhat.com>
|
Warning Review limit reached
Next review available in: 21 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughChangesThe pull request adds a complete Go v1 SDK module with protobuf contracts, shared types, authentication, resource clients, conversion helpers, streaming execution and networking, tests, examples, integration scaffolding, documentation checks, and protobuf generation tasks. Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant SDKClient
participant Converter
participant Gateway
participant Stream
Caller->>SDKClient: create client or call resource API
SDKClient->>Converter: convert SDK request
SDKClient->>Gateway: invoke RPC
Gateway-->>SDKClient: return response or stream
SDKClient->>Converter: convert response
SDKClient-->>Caller: return SDK result
SDKClient->>Stream: exchange exec or network frames
Possibly related issues
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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 |
Applied fixes from bot review comments: - Comment #3563985966: fix goroutine leak in interactiveSession readLoop - Comment #3563985982: add negative pagination validation in providerClient.List Assisted-By: 🤖 Claude Code Signed-off-by: Roland Huß <rhuss@redhat.com>
Applied fixes from bot review comments: - Comment #3563996825: make interactiveSession.ExitCode() idempotent - Comment #3563996843: map FailedPrecondition to ErrorConflict Assisted-By: 🤖 Claude Code Signed-off-by: Roland Huß <rhuss@redhat.com>
Applied fixes from bot review comments: - Comment #3564009036: acquire sendMu in interactiveSession.Close() Assisted-By: 🤖 Claude Code Signed-off-by: Roland Huß <rhuss@redhat.com>
Applied fixes from bot review comments: - Comment #3564015967: wait for readLoop goroutine in Close() Assisted-By: 🤖 Claude Code Signed-off-by: Roland Huß <rhuss@redhat.com>
Applied fixes from bot review comments: - Comment #3564038912: guard nil token from TokenSource in auth refresh - Comment #3564038916: map codes.Unauthenticated in gRPC error converter - Comment #3564038918: use hasExit flag instead of -1 sentinel in ExecResultFromEvents - Comment #3564038920: use append for nil-safe proto repeated field conversion - Comment #3564038923: fix incorrect timestamp comment in time_test.go - Comment #3564038931: reject partial cert/key mTLS configuration - Comment #3564038941: add 30s timeout to revoke cleanup calls Assisted-By: 🤖 Claude Code
Applied fixes from bot review comments: - Comment #3564088995: add zero-length buffer guard to interactiveSession.Read Assisted-By: 🤖 Claude Code
Lower the minimum Go version to 1.24.0 for broader compatibility. Downgraded dependencies: - google.golang.org/grpc v1.81.1 -> v1.80.0 - golang.org/x/oauth2 v0.36.0 -> v0.35.0 - google.golang.org/genproto v20260226 -> v20260120 - golang.org/x/net v0.51.0 -> v0.49.0 (indirect) - golang.org/x/sys v0.42.0 -> v0.41.0 (indirect) - golang.org/x/text v0.34.0 -> v0.33.0 (indirect) All 250 unit tests pass. Build succeeds. Assisted-By: 🤖 Claude Code
Lower the minimum Go version to 1.24.0, matching PR #9. Assisted-By: 🤖 Claude Code
Lower the minimum Go version to 1.24.0, matching PR #9. Assisted-By: 🤖 Claude Code
Lower the minimum Go version to 1.24.0, matching PR #9. Assisted-By: 🤖 Claude Code
Lower the minimum Go version to 1.24.0, matching PR #9. Assisted-By: 🤖 Claude Code
Lower the minimum Go version to 1.24.0, matching PR #9. Assisted-By: 🤖 Claude Code
Lower the minimum Go version to 1.24.0, matching PR #9. Assisted-By: 🤖 Claude Code
Lower the minimum Go version to 1.24.0, matching PR #9. Assisted-By: 🤖 Claude Code
Lower the minimum Go version to 1.24.0, matching PR #9. Assisted-By: 🤖 Claude Code
Lower the minimum Go version to 1.24.0, matching PR #9. Assisted-By: 🤖 Claude Code
There was a problem hiding this comment.
🧹 Nitpick comments (1)
sdk/go/go.mod (1)
3-5: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winValidate Go 1.24 compatibility under the pinned toolchain.
go 1.24.0declares the minimum version, buttoolchain go1.26.4makes Go automatically select or download Go 1.26.4 by default. Consequently, the module’s normal build/test path does not verify Go 1.24 compatibility. Either align/remove the toolchain directive, or add CI coverage usingGOTOOLCHAIN=localwith Go 1.24. The Go documentation distinguishes these directives and documents this automatic switching behavior. (go.dev)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/go/go.mod` around lines 3 - 5, Update the Go module/toolchain configuration around the go and toolchain directives so the normal validation path actually tests Go 1.24 compatibility. Either align or remove toolchain go1.26.4, or add CI coverage that runs with Go 1.24 and GOTOOLCHAIN=local; preserve newer-toolchain support separately if required.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@sdk/go/go.mod`:
- Around line 3-5: Update the Go module/toolchain configuration around the go
and toolchain directives so the normal validation path actually tests Go 1.24
compatibility. Either align or remove toolchain go1.26.4, or add CI coverage
that runs with Go 1.24 and GOTOOLCHAIN=local; preserve newer-toolchain support
separately if required.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 14f5bfdc-f5a9-4648-9736-5e6e81f095e4
⛔ Files ignored due to path filters (1)
sdk/go/go.sumis excluded by!**/*.sum
📒 Files selected for processing (2)
sdk/go/go.modsdk/go/openshell/v1/exec_client.go
🚧 Files skipped from review as they are similar to previous changes (1)
- sdk/go/openshell/v1/exec_client.go
Applied fixes from bot review comments: - Comment #3564256792: derive interactive session context from caller's ctx to prevent goroutine leak Assisted-By: 🤖 Claude Code
| opt = &opts[0] | ||
| } | ||
|
|
||
| stream, err := e.client.ExecSandboxInteractive(ctx) |
There was a problem hiding this comment.
🔴 Closing an interactive command session can hang forever because the underlying connection is not cancellable
The interactive session's network stream is opened with the caller's original context (e.client.ExecSandboxInteractive(ctx) at sdk/go/openshell/v1/exec_client.go:99) instead of a cancellable child context, so when Close() cancels its internal context (sdk/go/openshell/v1/exec_client.go:325), the receive loop remains blocked on the uncancellable stream and Close() waits on it forever (<-s.done at sdk/go/openshell/v1/exec_client.go:326).
Impact: Calling Close() on an interactive exec session can block indefinitely, leaking the goroutine and the gRPC stream.
Mechanism: stream context not derived from cancellable child
The sibling methods Stream() (exec_client.go:75-76) and Forward() (tcp_client.go:51-52) both follow the correct pattern:
streamCtx, cancel := context.WithCancel(ctx)
stream, err := e.client.ExecSandbox(streamCtx, req)This ensures that calling cancel() in Close() terminates the gRPC stream, causing Recv() to return a context error and unblocking the read loop.
However, Interactive() at exec_client.go:99 passes the raw parent ctx:
stream, err := e.client.ExecSandboxInteractive(ctx)Then newInteractiveSession at exec_client.go:194 creates a separate child context that is never wired to the stream:
ctx, cancel := context.WithCancel(parentCtx)When Close() calls s.cancel() at line 325, it cancels this orphan child context, but the stream's Recv() is governed by the original parent context and keeps blocking. Close() then blocks on <-s.done at line 326 until either the server closes its side or the parent context is cancelled externally.
The fix is to create the stream with a child context, matching the pattern used by Stream() and Forward().
Prompt for agents
In exec_client.go, the Interactive() method at line 99 creates the gRPC stream with the parent context ctx, but it should use a cancellable child context so that Close() can terminate the stream. The fix should mirror the pattern used by Stream() (lines 75-76) and Forward() in tcp_client.go (lines 51-52):
1. In Interactive(), before calling e.client.ExecSandboxInteractive, create a child context: streamCtx, streamCancel := context.WithCancel(ctx)
2. Pass streamCtx to ExecSandboxInteractive instead of ctx
3. On error from ExecSandboxInteractive, call streamCancel() before returning
4. Pass streamCancel (or streamCtx) to newInteractiveSession instead of creating a second child context inside newInteractiveSession
5. In newInteractiveSession, use the provided cancel function and context instead of creating new ones from parentCtx
This ensures that when Close() calls s.cancel(), it cancels the context that governs the stream, causing Recv() to return and unblocking the readLoop goroutine.
Was this helpful? React with 👍 or 👎 to provide feedback.
- Make tcpForwardConn.Close idempotent with sync.Once, matching sibling types - Guard against nil token dereference in refreshableAuth.GetRequestMetadata - Add mutex locks to all mockSandboxServer methods for race safety - Map codes.Unauthenticated to ErrorPermissionDenied in gRPC error converter - Replace exitCode == -1 sentinel with explicit hasExit boolean flag Assisted-By: 🤖 Claude Code Signed-off-by: Roland Huß <rhuss@redhat.com>
The example_test.go and example_fake_test.go files import the fake/ package which arrives in PR 2/3. Add a //go:build fakeclient constraint so PR 1/3 builds and tests cleanly on its own. The tag will be removed when the fake package lands. Assisted-By: 🤖 Claude Code Signed-off-by: Roland Huß <rhuss@redhat.com>
Summary
Add the Go SDK module with proto bindings, domain types, internal converters, and all core API clients.
Part 1 of 4 for #2044. Review this PR first.
What's Included
go.mod,go.sum,Makefile,mise.tomlundersdk/go/.pb.go(committed, no protoc needed)types/package with all SDK structs, errors, options, interfacesinternal/converter/(proto-to-SDK conversion) andinternal/grpc/(connection management)AuthProviderinterface, token refresh wrapper, extra-headers decoratorFile Tree
117 files | Module:
github.com/NVIDIA/OpenShell/sdk/goPR Chain
Summary by CodeRabbit